2

Method where scopes defined explicitly.

static void Main(string[] args)
{
    Class1 c1 = new Class1(1);

    {
        Class1 c2 = new Class1(2);

        {
            Class1 c3 = new Class1(3);
        }

    //this is not collecting object c3 which is out of scope here
    GC.Collect();
    }

    //this is not collecting object c2 which is out of scope here
    GC.Collect();
    Console.ReadKey();
}

Class1 definition:

class Class1
{
    int x;
    public Class1(int a)
    {
        x = a;
    }
    ~Class1()
    {
        Console.WriteLine(x + "object destroy");
    }
}

I write this code. But GC.Collect() method don't collect the object which goes out of scope.

Kote
  • 2,116
  • 1
  • 19
  • 20
huzefa
  • 49
  • 2
  • 7
  • 1
    Actually if you built in release mode without a debugger attached you would see c1 c2 and c3 all get cleaned up at the first GC.Collect() because a variable "goes out of scope" after the last time it is used in the function when in release mode. – Scott Chamberlain Jan 30 '16 at 06:09
  • @huzefa: How do you know "method don't collect the object which goes out of scope"? How did you test that? – CharithJ Jan 31 '16 at 22:00
  • @CharithJ - i write a msg in Class1 destructor. and i don't get that msg in console screen.by that i know the object don't get garbage collected. – huzefa Feb 02 '16 at 05:25
  • @huzefa: Have a look at my updated answer. – CharithJ Feb 02 '16 at 05:42
  • @CharithJ as u say in your updated answer that Garbage collector doesn't take inner scopes into account when clearing memory. But look at my answer also when i refer object with null GC collect the object. so why it collect only when object refer by null but not when it goes out of scope? is referring null is different from "out of scope" ?? – huzefa Feb 02 '16 at 05:53
  • @huzefa:Yes, setting null is different. It changes the reference of c2 and as a result the previously created object doesn't have any references. So, GC is certain that it is not referred by anything any more. Whereas in the previous case, GC doesn't want to take a risk and execute the finalizer while you are in the same local scope (in the same method with a reference). – CharithJ Feb 02 '16 at 05:59
  • 1
    **Collection does not run finalizers**. That is your first fundamental misunderstanding. Collection makes objects eligible for finalization by the finalizer thread. Your second fundamental misunderstanding is that **scope and eligibility for collection have almost nothing to do with each other**. The runtime is perfectly free to extend or shorten the lifetimes of references in locals as it sees fit. – Eric Lippert Feb 02 '16 at 06:35
  • If you want a correct understanding of finalization semantics then you might start by reading my articles on the subject. http://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/ – Eric Lippert Feb 02 '16 at 06:38

3 Answers3

1

GC.Collect() isn't really meant to be used for deterministic reclamation of resources. (That is what your finalizers in your real code is doing, right?)

If you have an unmanaged resource to deal with, strong consider using one of the classes in Microsoft.Win32.SafeHandles.

If you want deterministic release of resources, you should implement IDisposable, and call Dispose() when you are done using the object. The idiomatic way of doing this is:

using (var someResource = new Class1(1)) 
{
    // work with the `Class1` object
}

which is functionally equivalent to

{
    var someResource = new Class1(1);
    try 
    { 
        // work with the `Class1` object 
    }
    finally
    {
        someResource.Dispose();
    }
}
jdphenix
  • 15,022
  • 3
  • 41
  • 74
1

What you think about scoping is correct. Objects are going out of scope exactly as you expect. However, apparently the way you are testing it is not so correct.

Couple of points to consider here.

  1. You should implement the finalizer only if you have any unmanaged resources. If your class has a finalizer and if you don’t implement IDisposable Correctly in a way that it the suppress the finalization, instances of that class can reside in memory for two garbage collection cycles. Because, the first GC.Collect add that instance to the finalization queue if it requires finalization. May be the second garbage collection cycle actually clears the memory.

  2. Finalizer is the last point where .net objects can release unmanaged resources. Finalizers are to be executed only if you don’t dispose your instances correctly. Ideally, finalizers should never be executed. Because proper dispose implementation should suppress the finalization.

  3. According to your sample code, if you call GC.Collect and WaitForPendingFinalizers your test may pass (not sure how to find if memory is cleared).

Better test for you.

Garbage collector doesn't take inner scopes into account when clearing memory. If you move your code into a separate method it executes the finalizer.

Test 1

    static void Main(string[] args)
    {
        GCTest();

        Console.ReadKey();
    }


    private static void GCTest()
    {
        Class1 c1 = new Class1(1);

        {
            Class1 c2 = new Class1(2);

            {
                Class1 c3 = new Class1(3);
            }

            //this is not collecting object c3 which is out of scope here
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        //this is not collecting object c2 which is out of scope here
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

Test 2

Use using statement if you want to specify scoping for garbage collection purposes.

    private static void Main(string[] args)
    {
        using (Class1 c1 = new Class1(1))
        {
            using (Class1 c2 = new Class1(2))
            {
                Class1 c3 = new Class1(3);
            }
        }

        Console.ReadKey();
    }

    class Class1 : IDisposable
    {
        int x;
        public Class1(int a)
        {
            x = a;
        }

        public void Dispose()
        {
            Console.WriteLine(x + "object disposing");
        }
    }

Output

2object disposing

1object disposing

Community
  • 1
  • 1
CharithJ
  • 46,289
  • 20
  • 116
  • 131
0

Theses scopes do not make it into IL. The runtime has no idea they were there. At the IL level there are just locals and stack contents. Although the .NET JIT is poor in general it does a decent job of optimizing locals and temporary values into an efficient form.

There are no guarantees when objects will be collected. GC.Collect can't force that. There are some safe patterns to have it 99.999% guaranteed but never fully.

You never need this facility. In fact the need to have a finalizer is almost zero. Use the SafeHandle infrastructure.

If you really want to see the effect, move all of this code into a helper method and call that from Main. Back in main the Collect will succeed.

usr
  • 168,620
  • 35
  • 240
  • 369