The Garbage Collector

You will learn about the working of the garbage collector in D in this lesson.

Garbage collection cycle

The dynamic variables that are used in D programs live on memory blocks that are owned by the garbage collector (GC). When the lifetime of a variable ends (i.e., it’s no longer being used), that variable is subject to being finalized according to an algorithm that is executed by the GC. If nothing else needs the memory location containing the variable, the memory may be reclaimed to be used for other variables. This algorithm is called garbage collection, and an execution of the algorithm is called a garbage collection cycle.

The algorithm that the GC executes can roughly be described by the following steps. All of the memory blocks that can be reached directly or indirectly by pointers (including references) that are in the program roots are scanned. Any memory block that can be reached is tagged as being still in use and all the others are tagged as not being used anymore. The finalizers of objects and structs that live on inaccessible blocks are executed, and those memory blocks are reclaimed to be used for future variables. The roots are defined as all of the program stack for every thread, all of the global and thread-local variables, and any additional data added via GC.addRoot or GC.addRange.

Some GC algorithms can move objects around to keep them together in one place in memory. To preserve program correctness, all of the pointers (and references) that point to such objects are automatically modified to point to the new locations. D’s current GC does not do this.

A GC is said to be “precise” if it knows exactly which memories contain pointers and which do not. A GC is conservative if it scans all memories as if they were pointers. D’s GC is partially conservative, scanning only blocks that contain pointers, but it will scan all data in those blocks. For this reason, in some cases blocks are not ever collected, thereby “leaking” that memory. Large blocks are more likely to be targeted by “false pointers”. In some cases it may be recommended to manually free large blocks you are no longer using to avoid this problem.

The order of executing the finalizers is unspecified. For example, a reference member of an object may be finalized before the object that contains that member. For that reason, no class member that refers to a dynamic variable should be accessed inside the destructor. Note that this is very different from the deterministic destruction order of languages like C++.

A garbage collection cycle can be started for various reasons, including needing to find space for more data. Depending on the GC implementation, , all of the running threads may have to be halted during collection cycles because allocating new objects during a garbage collection cycle can interfere with the collection process itself.

Sometimes, this can be observed as a hesitation in the execution of the program. In most cases, the programmer does not need to interfere with the garbage collection process. However, it is possible to delay or dispatch garbage collection cycles as needed by the functions defined in the core.memory module.

Starting and delaying garbage collection cycles

It may be desirable to delay the execution of garbage collection cycles during a part of the program where it is important for the program to be responsive. GC.disable disables garbage collection cycles and GC.enable enables them again:

Get hands-on with 1200+ tech skills courses.