Well, in programming terms, anyway.
The garbage collector ensures that all memory and resources are returned when they are no longer needed and everything is wonderful.
Well, almost. It does make it a lot easier not to get into a bad situation but there are still ways of going horribly wrong. The managed environment is variously called NDP (.NET redistributable Package) or URT (Universal Runtime) or Dotnet FX (short for framework). I will call it the CLR because that is what we tend to call it internally. Anyway, it doesn’t reference count. Objects are destroyed when they are no longer accessible. If you can’t get at it then you don’t need it so that makes good sense as a strategy. Of course, the object doesn’t really go away when no-one can reach it. Instead, it becomes available for finalisation (ok, finalization) and destruction. It will be cleaned up at some future time and you can’t be sure when unless you force a full GC. Generally you don’t want to force a full GC because they are not cheap. Memory is cheap so it can be a good compromise to let things get a bit messy and clean up once in a while. Then again, that can burn you.
Imagine that there is a resource that your process doesn’t have in unlimited quantities. For the sake of illustration, we will assume that we are writing a killer application to help you pack a suitcase. Let us assume that there are 4 compartments available. You have some logic in your stateless component that grabs compartment object on entry in to a procedure (in to a local) , calls the AddHolidayObject method and then returns to the caller. The Object goes out of scope and so the GC can collect it. Now, the implementation of the Compartment object references a compartment handle since we are running on Windows for Luggage. By the way, we haven’t announced that version of Windows and will deny it if anyone asks. Anyway, the Compartments Objects hold a Compartment Object until they are cleaned up for you. Will the GC clean up 4 objects? Not unless you force it to because they don’t take up much memory. The CLR has no way on knowing that this object is hogging a vital resource. So, what happens when you try to add a 5th item? It will almost certainly fail because there are 4 handles which haven’t been given back to the OS yet.
Well, that is easy enough to fix. You add a close or dispose method and call that before you leave scope. That is easy enough to do. Even if you don’t it won’t generally matter that much unless the resource held by the object is really rare.
So, it is much harder to shoot yourself in the foot with managed code. That means that we support types get to play pool all day, Ah, would that this were true. A couple of problems that we often see are blocked finalizers and heap fragmentation.
A finalizer is a thread that does clean up of things for you. Objects have a finalize method. MSDN tells us about this:
Overrides Protected Sub Finalize()
protected override function Finalize();
Finalize is protected and, therefore, is accessible only through this class or a derived class.
This method is automatically called after an object becomes inaccessible.
Objects that need to be finalized are moved to a special queue called the finalizer queue. I admit that the names are not imaginative. The finalizer thread works through the queue. Most people never give a thought and if they do, they think of it as being part of the GC. Generally, the finalize method is cleaning up unmanaged stuff.
If something goes badly wrong while running the finalize method then the thread can stall. In a previous blog, I talked about hangs and how they can be caused by a bunch of things. Let us imagine (for the sake of argument) that the thing being cleaned up were part of a circular linked list. Due to a logic bug, the item that we are trying to remove has already been removed. This isn’t so unlikely because we are in cleanup and someone may have been a good citizen and tried to free the resource earlier. The linked list search routine in this hypothetical example is not too bright and has no mechanism for coping with a missing element because it shouldn’t ever happen. Instead, it just races around and around the list looking for the item. The finalizer thread is now doing nothing useful. In practice, deadlocked finalizers are much more common.
Unlike a normal hang, processing doesn’t stop. Unfortunately, your process is dying and nothing you can do will save it. Without the finalizer doing its work, your process uses more and more memory and more and more resources until it fails with an out of memory error. As memory gets scarce, the system will generally start to thrash. It looks a lot like a leak.
When debugging a situation that looks like a blocked finalizer, take 3-5 dumps as you would for a busy hang. Have a look at what the finalizer thread is doing. If it seems to be doing the same thing in all the dumps then it is worth digging further.
I will talk about heap fragmentation in my next blog.