Garbage Collection in .NET – Part 2/4

Table of contents of Part 1

Table of contents of Part 2

Table of contents of Part 3

Table of contents of Part 4

 

The Components of the Garbage Collector

In the previous chapter, the Generations‘ role was covered in the Garbage Collection. In this chapter, the role of the other three components – GC Roots, Object Sync Block, and Finalization, F-Reachable Queues – in the Garbage Collection will be covered.

GC Roots

As mentioned in the first chapter, the Garbage Collector needs to build an object tree to determine which objects are in use and which are not. To do that, it needs information like static and local variables which are all known as GC Roots. They’re a starting point for the Garbage Collector to build the object graph.

Object tree model
Figure 3: Object tree model

In Figure-3, a small model of the object tree that the Garbage Collector uses is shown. In this diagram, root objects refer to object [A, D, E], and object [D] refers to object [C]. According to this, the objects [A, D, E] cannot be collected by Garbage Collector because they are referenced by the root objects. Besides, object [C] cannot be collected as well, because it is referred by an object that is referenced by root objects. Since the object [B] isn’t referenced by any root object or objects which are directly or indirectly referenced by root objects, it is marked as ‘0’ (zero) which means it is unused. That means object [B] will be collected on the next Garbage Collector run.

The indicator values ‘0’ and ‘1’ are placed in the Object Sync Block which will be explained in the next section.

Here are the definitions of all root objects.

Local Variables

Local variables are considered as root objects by the Garbage Collector as long as the method they are declared in is on the Thread Stack. The Garbage Collector examines all methods on the Thread Stack and uses local variables as roots when it builds the object graph.

Static Variables

They are created when the type that they are declared in is loaded and they remain alive in the memory for the entire lifetime of the AppDomain. AppDomain provides a layer of isolation within a .NET process. Details for AppDomains can be found here.

GC Handles

They are a kind of protection mechanism for objects that need to be transferred from the managed environment to the unmanaged environment. When the CLR initializes a new .NET process, it provides the GC Handle Table for each AppDomain.

F-Reachable Queue

The F-reachable queue is a part of the Garbage Collector finalization mechanism. This structure is used to collect dead objects that have a Finalizer method. Details about the finalization mechanism and the finalizer method can be found in the next section.

Object Sync Block

It’s a 4-bytes value that is used for different purposes. This article concentrates on the purpose that it is used in the Garbage Collector algorithm’s Mark phase. Mark Phase is a phase during Garbage Collections that the marked bit is set to 1 (true) for all reachable objects (objects which a user can refer to). The following figures can help to have a better understanding of the purpose of Sync Block in the Mark Phase.

Code block
Figure 4: Code block

Sync Block representation
Figure 5: Sync Block representation

In Figure-4, a Person class with an Id property is given. If this class is instantiated, a memory block is allocated in the managed heap as shown in Figure-5. Since the Id property isn’t set, the memory block of the ‘Int32 Id’ is ‘0’ and the ‘Sync Block Index’ is ‘0’ too. If the Id property is set to a value (e.g. 7), then the memory block of the ‘Int32 Id’ will change to ‘7’ and the ‘Sync Block Index’ will change to ‘1’ which means this instance of the Person class cannot be collected by the Garbage Collector.

The above scenario is one of the usages of Sync Block. The Sync Block has four usages. Those are;

  • Thread synchronization
  • Object hash code storing
  • Garbage Collection algorithm
  • Garbage Collection finalization algorithm

Finalization Queue and F-Reachable Queue

The purpose of these queues is to manage finalizable objects – objects with a Finalizer method and the main difference between those queues is the objects that they handle. The Finalization Queue handles the objects which have a finalizer. Every object with a Finalizer method is added to this queue. The F-Reachable Queue handles the garbage objects which have a finalizer. Every garbage object with a Finalizer method is added to this queue. When a new object is created, a reference to that object is added into the Finalization queue. After that object died, the reference is moved from the Finalization queue to the F-Reachable queue.

The thing that must be kept in mind for those structures is that each queue has its dedicated thread. Due to misusage of the Finalizer method, those threads might be blocked and the memory space occupied by those objects might not be released. That’s why the use of the Finalizer method must be avoided unless it is needed. The use of the Dispose pattern should be preferred.