How Does a Garbage Collector Work?
A garbage collector (GC) is a memory manager. Many programming languages have a built-in GC. This feature automatically allocates and deallocates the memory in a program. It releases tied-up, unused memory that slows down your application.
The beauty of a GC is that it releases memory on your behalf, without you needing to do anything. You might, therefore, consider it such an essential feature that you’d expect every programming language to have it. Sadly, this is not the case; even a popular language like C can lack a GC.
How Does Memory Allocation Work?
When you run a program in any programming language, your operating system reserves a data stack in memory for that program. This program owns and occupies this data stack until it completes execution. If your program needs more memory than what is available, it can dynamically allocate more memory from the operating system’s memory heap.
In programming, a variable represents a memory location. So, when you declare a new variable, the programming language allocates space in memory for this variable. The variable will now have a memory address. Until you assign a value to this variable it will remain uninitialized, and it might contain some garbage value.
If a programming language allows you to declare a variable without initializing it, then it is a dynamic variable. This means that the value you assign to the variable might change over time. However, the memory location of the variable will remain the same until you deallocate it.
How Does Memory Deallocation Work?
Memory allocation is a similar process for all programming languages. But the corresponding method of memory deallocation tends to differ. There are two types of memory deallocation methods; manual and automatic. A GC does automatic deallocation.
Memory Deallocation Without a Garbage Collector
The C programming language does not use a GC for memory deallocation. Therefore, C programmers must manually allocate and deallocate memory. C allows dynamic memory allocation for when you do not know, at compile time, how much memory you will use at run time.
The standard library (stdlib.h) contains the functions that C uses to manage dynamic memory allocation. These functions include:
- malloc(): allocates a specific size of memory and returns a pointer to that memory. If there is insufficient memory available in the operating system memory pool, it returns null.
- free(): deallocates a specific block of memory and returns it to the operating system memory pool.
C Program Example
ptr = (int *) malloc(200 * sizeof(int));
for (j = 0; j < 200; j++)
ptr[j] = j;
The code above allocates memory to store 200 integer values using the malloc() function. It uses a pointer to access this memory location and stores 200 integer values in it. The pointer also prints the data stored at the memory location to the console. Finally, the program deallocates the previously allocated memory using the free() function.
Memory Deallocation With a Garbage Collector
Several popular programming languages use a GC for memory management. This makes the life of programmers who use these languages much easier. C# and Java are two programming languages that use a GC.
The C# GC
In the C# programming language, a GC manages the allocation and deallocation of memory addresses. Therefore, a C# programmer does not need to worry about deallocating an object after it completes its purpose.
The C# GC initializes a memory pool, called the managed heap, for every new process (or program). It calls the VirtualAlloc() function to allocate memory and the VirtualFree() function to deallocate it. The best part is that this all happens in the background with no effort required from you, the programmer.
The C# GC has an optimizing engine, which it uses to decide when to deallocate memory. The optimizing engine examines the application root to determine which objects are no longer in use. It does this by creating a graph that extends from the root of the application to connected objects. This root includes static fields, local variables, etc. Any object not connected to the application root is garbage.
The GC optimizing engine does not just collect memory on its own. There must first be a new memory allocation request. If the system has a low amount of available memory, then the GC optimizing engine will come into play.
The Java GC
In Java, a GC also manages the allocation and deallocation of memory addresses. However, Java currently has four different types of supported garbage collectors:
- Garbage-First (G1)
- Z Garbage Collector (ZGC)
The G1 garbage collector is Java’s default GC since the release of the Java Development Kit (JDK) 9. Java organizes data in objects and stores these objects in a fixed-size heap. The G1 garbage collector divides the heap into equal-size heap regions. It then split these heap regions into two sections; young and old generations.
Each time you create a new object, space allocation for this object happens in the young generation. Using an aging process, the G1 garbage collector copies objects in the young regions to the old regions. It also copies objects that are already in the old region to an older region.
The G1 garbage collector then performs most of its memory deallocation in the young generation, occasionally venturing to the old generation section.
What Are the Benefits of Having a Garbage Collector?
The benefit of having a garbage collector is that it prevents you from thinking about memory management while writing your code. This gives you time to focus on the other important aspects of your application. However, several other benefits are worth highlighting.
Reclaiming unused objects and freeing memory provides cleaner application execution. If your program frees memory as soon as possible, it will have a smaller memory footprint and can run more efficiently.
Garbage collection reduces errors related to memory management such as leaks and pointer errors. This is because the process is no longer dependent on the programmer and their ability to write accurate code.