Garbage Collection

Garbage collection is obviously a core function of the runtime. Its purpose is to restore memory consumed by objects that are no longer referenced. The emphasis in this statement lies with memory and references. The garbage collector is only responsible for restoring memory; it does not handle other resources such as database connections, handles (files, windows, and so on), network ports, and hardware devices such as serial ports. Also, the garbage collector determines what to clean up based on whether any references remain. Implicitly this means that the garbage collector works with reference objects and restores memory on the heap only. Additionally, it means that maintaining a reference to an object will prevent the garbage collector from reusing the memory consumed by the object.

Advanced Topic: Garbage Collection in .NET

Many details about the garbage collector pertain to the specific CLI implementation, and therefore, they could vary. This section discusses the .NET implementation, since it is the most prevalent.

In .NET, the garbage collector uses a mark-and-compact algorithm. At the beginning of a garbage collection cycle, it identifies all root references to objects. Root references are any references from static variables, CPU registers, and local variables or parameter instances (and f-reachable objects). Given this list, the garbage collector is able to walk the tree identified by each root reference and determine recursively all the objects to which the root references point. In this manner, the garbage collector identifies a graph of all reachable objects.

Instead of enumerating all the inaccessible objects, the garbage collector performs garbage collection by compacting all reachable objects next to each other, thereby overwriting any memory consumed by objects that are inaccessible (and, therefore, are garbage).

Locating and moving all reachable objects requires that the system maintain a consistent state while the garbage collector runs. To achieve this, all managed threads within the process halt during garbage collection. This obviously can result in brief pauses in an application, which is generally insignificant unless a particularly large garbage collection cycle is necessary. In order to reduce the likelihood of a garbage collection cycle at an inopportune time, however, the System.GC object includes a Collect() method, which can be called immediately before the critical performing code. This will not prevent the garbage collector from running, but it will reduce the likelihood that it will run, assuming no intense memory utilization occurs during the critical performance code.

One perhaps surprising aspect of .NET garbage collection behavior is that not all garbage is necessarily cleaned up during an iteration. Studies of object lifetimes reveal that recently created objects are more likely to need garbage collection than long-standing objects. Capitalizing on this behavior, the .NET garbage collector is generational, attempting to clean up short-lived objects more frequently than objects that have already survived a garbage collection iteration. Specifically, there are three generations of objects. Each time an object survives a garbage collection cycle, it is moved to the next generation, until it ends up in generation two (counting starts from zero). The garbage collector then runs more frequently for objects in generation zero than it does for objects in generation two.

Ultimately, in spite of the trepidation that .NET faced during its early beta releases when compared with unmanaged code, time has shown that .NET's garbage collection is extremely efficient. More importantly, the gains created in development productivity have far outweighed the costs in development for the few cases where managed code is dropped to optimize particular algorithms.

Weak References

All references discussed so far are strong referencesbecause they maintain an object's accessibility and they prevent the garbage collector from cleaning up the memory consumed by the object. The framework also supports the concept of weak references, however. Weak references will not prevent garbage collection on an object, but they will maintain a reference so that if the garbage collector does not clean up the object, it can be reused.

Weak references are designed for objects that are expensive to create and are too expensive to keep around. Consider, for example, a large list of objects loaded from a database and displayed to the user. The loading of this list is potentially expensive, and once the user closes the list, it should be available for garbage collection. However, if the user requests the list multiple times, a second expensive load call will always be required. However, with weak references, it is possible to use code to check whether the list has not yet been cleaned up, and if not, to rereference the same list. In this way, weak references serve as a memory cache for objects. Objects within the cache are retrieved quickly, but if the garbage collector has recovered the memory of these objects, they will need to be re-created.

Once an object (or collection of objects) is recognized for potential weak reference consideration, it needs to be assigned to System.WeakReference (see Listing 9.19).

Listing 9.19. Using a Weak Reference

// ...   private WeakReference Data;                                         public FileStream GetData()  {      FileStream data = (FileStream)Data.Target;                                if (data != null)                                                          {           return data;      }      else      {           // Load data           // ...           // Create a weak reference                                                     // to data for use later.                                                      Data.Target = data;                                                  }      return data;      }  // ...

Given the assignment of WeakReference (Data), you can check for garbage collection by seeing if the weak reference is set to null. The key in doing this, however, is to first assign the weak reference to a strong reference (FileStream data = Data) to avoid the possibility that between checking for null and accessing the data, the garbage collector runs and cleans up the weak reference. The strong reference obviously prevents the garbage collector from cleaning up the object, so it must be assigned first (instead of checking Target for null).

Essential C# 2.0
Essential C# 2.0
ISBN: 0321150775
EAN: 2147483647
Year: 2007
Pages: 185 © 2008-2017.
If you may any questions please contact us: