views:

103

answers:

2

I am trying to write a unit test using .NET 4 to ensure that an object can be garbage collected after some code is run. In Java, I would use assertGC to ensure that a weak reference is collected. How can I write this type of test for .NET?

I have tried keeping a WeakReference to the object and calling GC.Collect(), but as you'd expect, sometimes my object is collected and sometimes it is not. Note that this is for a unit test, not production code. I would not want GC.Collect() in my real code base.

I'm using C# but the same answer will be good for VB.NET too.

A: 

Try this:

Running for 5 minutes solid, still no exception... Are you sure your code isn't referencing your object somewhere?

using System;

internal class Program
{
    private static void Main(string[] args)
    {
        int cnt = 0;
        while (true)
        {
            ++cnt;
            bool gced = false;
            Action handler = () => gced = true;
            new Foo(handler);
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.Out.WriteLine("{0} : {1}", cnt, gced);
            if (!gced)
            {
                throw new Exception("WTF?");
            }
        }
    }
}
class Foo
{
    private readonly Action _onFinalized;

    public Foo(Action finalized)
    {
        _onFinalized = finalized;
    }

    ~Foo()
    {
        if (_onFinalized != null) _onFinalized();
    }
}
Florian Doyon
Thanks for the answer. I have modified my object to include your field, constructor and finalizer. When I run this test on my machine, the finalizer (and therefore the Action) does not always run. On my machine in a form with just one button to run the test, it works the first and second time this is run in a process, but does not run on the third time. On the fourth time, the object from the third time is finalized, but the new object is not! The unit test works on my machine but does not work on the build machine. GC.Collect() is not triggering the finalizer.
Andrew
What about the waitforpendinfinalizer?If you are in debug mode, the ref to `a` might be held by your debug vizualizers...
Florian Doyon
The code is all compiled with the debug flag, but the issue still occurs when a debugger is not attached to the application. Also - the issue is intermittent - it sometimes works.
Andrew
Updated the code above, been running this for 10 minutes both in release and debug mode... Test procedure: choose build, then ctrl-f5
Florian Doyon
@Florian Doyon, thanks for keeping with it, I'll test this and get back to you.
Andrew
Change the line 'new Foo(handler)' into:'var foo = new Foo(handler);for (var i = 0; i < 100000; i++ ) { new Foo(null); }'... and it fails for me. You need to get the Foo object out of the first GC generation to make it fail.
Andrew
A: 

It looks like this isn't possible at the moment with .NET 4. Though it is possible to know whether an object cannot be garbage collected (e.g. have a reference to it), there is no way to deterministically know whether an object can be garbage collected.

Using a WeakReference or a finaliser (and calling GC.Collect()) may collect the object, but unless it is in generation 0, there is a fair chance that it will not.

References:

Andrew