.NET Garbage Collector making me batty, again

I wrote previously about two things I had learned, through great pain, about the .NET garbage collector.

Now I have another lesson to add to that.

It a common requirement to have only a single instance of an application running.  If the user tries to launch another instance, the new instance is supposed to detect the presence of the previous instance, and then either do nothing, or perhaps bring the old instance to the foreground.

The usual mechanism to implement this is to use a named mutex.  The first instance to run creates a named mutex, using a predetermined and hopefully unique name.  Named mutexes, unlike nameless mutexes, are visible to the entire Windows system.  Later instances can detect the existence of that named mutex, and respond accordingly.

Here is the code I wrote to do this in C#:


static class Program
{
    ///
    /// The main entry point for the application.
    ///
    [STAThread]
        static int Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        bool createdNew;
        System.Threading.Mutex mutex = new System.Threading.Mutex( false, "Monitor", out createdNew );

        if( createdNew ) {
            Application.Run(new MonitorForm( logon ));
        }
        return 0;
    }
}

In testing using a Debug build, this all worked perfectly. But when I made a Release build, it all fell apart. New instances could not detect the original instance anymore.

SysInternals Process Explorer showed me something puzzling. When the first instance started, the named mutex was created as expected. But after a few seconds, it disappeared. All by itself, while the application was still running, it just evaporated.

I added some debugging code, after the Application.Run() call, which attempted to do some operations on the supposedly-gone mutex. Surprisingly, the test code worked. And, in fact, the named mutex no longer disappeared. It remained as long as the application was running, as it was supposed to.

Somehow I got the idea that the .NET garbage collector might be doing something surprising. I was expecting, from my long experience as a C++ programmer, that the mutex I created would be destroyed at the end of the block, after Application.Run() returned. But it occurred to me that maybe the garbage collector is really just way smarter than I ever imagined. Could the garbage collector actually be smart enough to realize that the mutex object I created can never actually be referenced again?

To test this, created a little test class. It did nothing, really, except just put a message on the system debug output when it was claimed by the garbage collector. Looks like this:


class FoobarTest
{
    public FoobarTest()
    {
        System.Diagnostics.Trace.WriteLine( "FoobarTest created." );
        x = 5;
    }

    ~FoobarTest()
    {
        System.Diagnostics.Trace.WriteLine( "~FoobarTest." );
    }

    public int x;
}

static class Program
{
    ///
    /// The main entry point for the application.
    ///
    [STAThread]
        static int Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        FoobarTest test = new FoobarTest();

        Application.Run(new MonitorForm( logon ));
        System.Diagnostics.Trace.WriteLine( "Application.Run returned." );
        return 0;
    }
}

What I, as a C++ programmer, would have expected to appear in SysInternals DebugView is this:


FoobarTest created.
Application.Run returned.
~FoobarTest.

What actually appears is this:


FoobarTest created.
~FoobarTest.
Application.Run returned.

So, there you have it. The .NET garbage collector is actually crazy smart. Even if a reference to an object remains in-scope, it can still detect that the object actually cannot be referenced again, and will garbage-collect it. I did not expect that.

If that code had been in a loop, or maybe even had a goto, such that execution could return to a point where the object was referenced again, the garbage collector would not have been able to collect it. Really freaky smart.

3 Responses to “.NET Garbage Collector making me batty, again”


  • Its probably not the garbage collector.
    Probably the compiler reduces the scope of the object to only the region where it is accessed. Which means, the reference to the object is gone before it actually “went out of scopoe”. I had seen this type of thing back as far as 1990, where I saw that my compiled basic program didn’t bother storing the result of a calculation because it knew it would never get referenced again.

  • You’re probably right. I’m pretty sure the garbage collector wouldn’t be examining the code to see what it does, or might do.

    Anyway, it was a behaviour that caught me by surprise. C++ certainly wouldn’t do that. In fact, it’s a fairly common technique to create objects for the sole purpose of running their destructors at the end of the scope. But they are not referenced at all after their creation.

    For example, I have a class like that that I use to acquire a semaphore, which guarantees that no matter how we leave its scope (return, goto, exception, whatever), the semaphore will be released. Foolproof, and no need for ugly try/catch blocks.

Leave a Reply

Categories

Archives