Tips for the Managed Heap
These are tips for interacting with the managed heap. Some of these tips, such as avoiding the GC.Collect method, have been articulated previously in this chapter. However, they are included here for completeness.
Do not program contrary to the garbage collection paradigm in the managed environment. Small objects should be short lived, while larger objects should be long lived. Objects are expected to communicate with like-sized objects.
Avoid boxing. Frequent boxing, as occurs when using non-generic collections with value types, flood Generation 0 with small objects. This will trigger extra garbage collections.
Because of the cost of finalization, use a Finalize method only when imperative. Furthermore, empty destructors are not innocuous as in C++. You still incur the full cost of finalization.
Classes that have a Finalize method should not have deep object graphs. Finalizable objects are kept in memory longer than normal objects. Objects referenced by the finalizable objects are also kept in memory longer.
If possible, do not refer to other managed objects in the Finalize method. First, those objects may no longer exist. Second, you may inadvertently create a back reference to yourself and resurrect the current object. Resurrected objects can be problematic.
Define disposable objects in the using statement, which will automatically call the Dispose method and guarantee cleanup.
Do not call GC.Collect. This is especially true for a complete garbage collection, which is expensive. Allow garbage collection to occur naturally.
Keep short-lived objects short lived. Do not reference short-lived objects from long-lived objects. That links the lifetime of the two objects, and both are then essentially long-lived objects.
Set objects as class members and local objects to null as early as possible. This allows them to be collected as soon as possible.
Do not allocate objects in either hashing or comparison methods. When sorting or comparing, these methods can be called repeatedly in a short period of time. If the methods contain allocations, this could result in considerable memory pressure on the managed heap and additional garbage collection activity.
Avoid near-large objects. These are objects that are close to 85 KB in size. As near-large objects, expect those objects to migrate to Generation 2. Add a buffer to the type and increase the near-large object to a large object. This will place the object immediately on the Large Object Heap and avoid the overhead of promoting the object through to Generation 2.
Keep code in a Finalize method short. All Finalize methods are serviced by a separate thread—the finalizable thread. An extended Finalize method prevents a thread from servicing other Finalize methods and releasing the reference to the related object.
Even after adhering to every tip, don’t be surprised to have the occasional memory problem. The CLR Profiler from Microsoft is helpful in those occasions. This tool allows developers to diagnose issues with the managed heap.