views:

67

answers:

1

Here is a great article about GC may occur at unexpected point of code execution:

Lifetime, GC.KeepAlive, handle recycling – by cbrumme http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx?wa=wsignin1.0

My question is how can I reproduce forced GC at the point mentioned in the article? I tried to put GC.Collect() at beginning of OperateOnHandle(), and defined destructor for class C but seems not working. Destructor is invoked always at end of program.

@Jon, thanks for advising me running out of debugger. Now I am able to reproduce the issue described in the article, using optimised Release build out of debugger. It proves this GC behaviour doesn't change since .NET v1.

Code I used:

class C1
{
    // Some unmanaged resource handle
    IntPtr _handle = IntPtr.Zero;

    static void OperateOnHandle(IntPtr h)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("After GC.Collect() call");

        // Use the IntPtr here. Oops, invalid operation
    }

    public void m()
    {
        OperateOnHandle(_handle);
    }

    ~C1()
    {
        // Release and destroy IntPtr here
        Console.WriteLine("In destructor");
    }
}

class Program
{
    static void Main(string[] args)
    {
        C1 aC = new C1();
        aC.m();
    }
}

Output:

In destructor After GC.Collect() call

+1  A: 

Bear in mind that that article is from 2003, which was using CLR v1. We're now on CLR v4 (although there was no v3) so I'm not entirely surprised you don't see exactly the same behaviour.

Currently I can't even get to the linked page, and you haven't included a description of what the page describes. Is it the possibility of an object being garbage collected before the end of an instance method?

If so, the most obvious reason you might have problems reproducing it is if you're using the debugger. The garbage collector is much more aggressive when you're running without a debugger attached.

Here's a short but complete program which demonstrates the issue when running not in a debugger:

using System;

class ClassWithFinalizer
{
    private int value;

    public ClassWithFinalizer(int value)
    {
        this.value = value;
    }

    ~ClassWithFinalizer()
    {
        Console.WriteLine("Finalizer running!");
    }

    public void ShowValue()
    {
        Console.WriteLine(value);
        Console.WriteLine("Calling GC.Collect()");
        GC.Collect();
        Console.WriteLine("Calling GC.WaitForPendingFinalizers()");
        GC.WaitForPendingFinalizers();
        Console.WriteLine("End of method");
    }
}

class Test
{
    static void Main()
    {
        var x = new ClassWithFinalizer(10);
        x.ShowValue();
    }
}

Compilation (optimized and without debug symbols, just to give it the best chance!):

csc /o+ /debug- Test.cs

Now run, with output:

c:\users\Jon\Test>test
10
Calling GC.Collect()
Calling GC.WaitForPendingFinalizers()
Finalizer running!
End of method

Note how the finalizer runs before the method has completed.

Tested with .NET 4 and .NET 3.5.

Jon Skeet
Put Console.ReadLine() at the end of Main() to repro the OP's problem.
Hans Passant
@Hans: Nope... I've just put Console.ReadLine() and the end, and it behaves the same way. The finalizer is called before the method finishes.
Jon Skeet
@Jon, thanks for your example.
Sheen