Garbage Collection

.Garbage Collection
Garbage Collection | Finalizers | IDisposable

"Garbage Collection divides the managed heap into three generations. Gen0 for objects just allocated, Gen1 for objects which survive on collection cycle, and Gen2 for all other objects."

The Common Language Runtime (CLR) contains a Garbage Collector (GC) which wakes up at certain times and performs automatic memory management for objects stored on the heap. The GC runs on its own thread and blocks all other threads while running. The GC divides the heap into three segments corresponding to the object generations. Unused objects without finalizers are discarded while unused objects with finalizers enqueued for processing on the finalizer thread which runs after the GC thread is finished. The finalizer thread runs concurrently with the application threads. One the object 's finalizer method has completed, the object is orphaned and will get deleted on the next GC cycle.

The GC marks objects which are still alive with a generation number. Gen0 for objects just allocated, Gen1 for objects which survive on collection cycle, and Gen2 for all other objects. The objects which are determined to be alive are shifted to the front of their respective heap segments. The Gen0 segment is relatively small. When the Gen0 heap segment fills up, a Gen0 GC cycle runs. This happens fairly frequently and a Gen0 cycle runs quickly. Also Gen1 collects happen fairly frequently and run quickly. A full GC cycle, which includes all three generations takes a considerably longer amount of time. There is also a special area of the heap for large objects which does not get compacted.

In C# finalizers (similar in function to Destructors in C++ and PHP) are methods called during the destruction of an object. The finalizer thread runs at the same time as the application threads (unlike the GC which blocks application thread when it runs). Generally finalizers are not used in C# unless a special circumstance requires their usage. finalizers do slow the allocation and collection of memory. Also if a finalizer is not able to complete, it will block the remaining objects in the finalizer queue.

Some object in C# do require explicit tear-down to release external resources. These objects include: files, locks, O/S handles (e.g. database connections) and unmanaged objects. This is supported in C# with the IDisposal interface which contains the specification for a Dispose method. Unlike GC, disposal of objects is usually explicitly called by calling the Dispose method on the object. A short cut for calling Dispose is the using statement. The using statement gets compiled into the equivalent code of calling the object's Dispose() method from the final clause of a try statement.

Using Statement - Ensures Disposal() Method is Called
using (FileStream fs = new FileStream ("filename.txt", FileMode.Open))
{
}




compiler converts to:

FileStream fs = new FileStream ("filename.txt", FileMode.Open);
try
{
}
finally
{
   if (fs != null) ((IDisposable)fs).Dispose();
}


Garbage Collection

The garbage collector serves as an automatic memory manager. It provides the following benefits:

  1. Enables you to develop your application without having to free memory.
  2. Allocates objects on the managed heap efficiently.
  3. Reclaims objects that are no longer being used, clears their memory, and keeps the memory available for future allocations. Managed objects automatically get clean content to start with, so their constructors do not have to initialize every data field.
  4. Provides memory safety by making sure that an object cannot use the content of another object.

Garbage collection occurs when one of the following conditions is true:

  1. The system has low physical memory.
  2. The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.
  3. The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.

The GC class controls the system garbage collector. The GC class contains the following methods:

  1. Collect() - forces immediate garbage collection for all generations.
  2. KeepAlive() - makes an object ineligible for garbage collection from the start of the current routine to the point where this method is called.
  3. SuppressFinalize() - requests that the common language runtime not call the finalizer for the specified object.
Top

Finalizer

A finalizer can be present in any class—it's an optional member that the garbage collector promises to call on otherwise dead objects before it reclaims the memory for that object. In C# you use the ~Class syntax to specify the finalizer. For various reasons, it is important to use finalizers sparingly and place them on objects that have as few internal object pointers as possible.

Objects that need finalization live longer than objects that do not. In fact, they can live a lot longer. For instance, suppose an object that is in gen2 needs to be finalized. Finalization will be scheduled but the object is still in gen2, so it will not be re-collected until the next gen2 collection happens. That could be a very long time indeed, and, in fact, if things are going well it will be a long time, because gen2 collections are costly and thus we want them to happen very infrequently. Older objects needing finalization might have to wait for dozens if not hundreds of gen0 collections before their space is reclaimed.

Top

IDisposable

The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. Instead, to implement the dispose pattern for a derived class, you provide the following:

  • A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. This method should also call the Dispose(Boolean) method of the base class and pass it a value of true for the disposing argument.
  • Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false.
  • Top



    Reference Articles

    Top