Key Points
- Garbage collection in Java is an advanced programming concept that allows for the removal of unnecessary information from a program, resulting in optimized code.
- The Garbage Collection tool in Java automatically selects which items should be removed from the program’s memory, but customizable parameters can be set to determine what type of objects to delete and when.
- The mark-and-sweep algorithm is the basis of the Garbage Collection process, where objects are marked as either reachable or not, and those with a mark of 0 (false) are removed from memory.
- Garbage Collection in Java can be optimized by dividing the heap into different generations or spaces, allowing for more efficient and less error-prone operations.
Garbage collection in Java is an advanced programming concept essential to creating optimized code. Through an automatic process, the Garbage Collection tool allows you to remove unnecessary information from your program with a few clicks.
While running the process itself is rather simple, the concept behind it has a lot of depth and is controlled by several customizable variables. There’s a lot to learn here, so let’s get started without further delay.
Fundamentals of Java Garbage Collection
Garbage Collection, in Java, is an automatic process that removes information or code we no longer need.
This operation assures us that any program will work with the highest efficiency and with the shortest loading times. Usually, the process runs on software that uses a Java Virtual Machine (JVM).
In other languages, such as C++ and C, garbage collection must be done manually, paying close attention to which objects we remove. This process can be tedious and time-consuming, so automation is a great advantage.
JVM programs have a dedicated section of memory that uses dynamic allocation we call the “heap.“ The Garbage Collection tool constantly communicates with the heap and automatically selects which items should be removed. This means we have no control once the program is running.
However, we can specify a series of parameters before running the Garbage Collector to determine what type of objects we want to delete and when.
But why should we use Garbage Collection?
Well, first and foremost, a program that does not regularly remove unused objects from its heap will have storage errors. But just as importantly, programs that don’t use Garbage Collection tools will be poorly optimized, resulting in slower and less efficient operations.
Garbage Collection: How Does It Work?
The job is simple: we need to detect which objects are no longer important for the proper functioning of the program and then remove them. But how does the collector manage to identify these objects?
When we first run the garbage collector, it starts by identifying the data connections between objects and other code components. If an object is not referenced from any other location, the collector deletes it and frees the memory space it was occupying.
The Garbage Collection tool mostly uses the “mark-and-sweep” algorithm to perform this task. Below you’ll find an explanation of this method, as understanding it is crucial to avoid frequent errors such as memory leaks.
Mark-And-Sweep Algorithm
The Garbage Collection process has a structure based on the mark-and-sweep algorithm. As its name indicates, this algorithm has a first phase, called Mark, and another called Sweep.
Whenever we create an object in the heap, we initialize the mark-and-sweep algorithm. In that first instance, the garbage collector examines the tree of an object, starting at its roots.
If the object in question is referenced in the roots, the mark (initially set to 0) changes to 1 (true).
In all other cases, the value of the mark remains 0. This means that other parts of the code do not reference the object. During the sweep phase, the collector removes all objects with mark 0 (false) and frees memory space.
A simplified mark-and-sweep algorithm looks like this:
import java.util.ArrayList;
import java.util.List;
public class GarbageCollector {
private List<Object> roots;
private List<Object> objects;
public GarbageCollector() {
roots = new ArrayList<>();
objects = new ArrayList<>();
}
public void addObject(Object object) {
roots.add(object);
objects.add(object);
}
public void runGarbageCollection() {
markPhase();
sweepPhase();
}
private void markPhase() {
for (Object root : roots) {
markObject(root);
}
}
private void markObject(Object object) {
if (object != null && objects.contains(object)) {
// Mark the object as referenced
objects.remove(object);
objects.add(object);
}
}
private void sweepPhase() {
List<Object> unreachableObjects = new ArrayList<>();
for (Object object : objects) {
int mark = objects.indexOf(object);
if (mark == -1) {
unreachableObjects.add(object);
}
}
for (Object unreachableObject : unreachableObjects) {
objects.remove(unreachableObject);
// Perform any additional cleanup or freeing of resources
}
}
public static void main(String[] args) {
GarbageCollector gc = new GarbageCollector();
// Create some objects
Object obj1 = new Object();
Object obj2 = new Object();
Object obj3 = new Object();
gc.addObject(obj1);
gc.addObject(obj2);
// obj3 is not added as a root, so it becomes unreachable
gc.runGarbageCollection();
System.out.println("Remaining objects after garbage collection:");
for (Object object : gc.objects) {
System.out.println(object);
}
}
}

©History-Computer.com
Here, the “runGarbageCollection” method initializes the “GarbageCollector” class, which stores all objects in two lists: “roots” and “objects.” The “addObject” method creates objects in the heap — which the “markPhase” and “sweepPhase” methods can work with.
The process works as previously explained: the “markPhase” method marks all reachable objects by storing them at the end of the “objects” list. The “sweepPhase” method then iterates through the list and identifies the objects with marks depending on whether they are reachable or not.
If they aren’t, it removes them from the list. Finally, the “main” method demonstrates all this by running this whole process and printing the objects left after the algorithm has done its job.
Garbage Collection Optimization
Garbage Collection in Java can also get cluttered (like any other programming tool). This results in a poorly-optimized experience — but luckily, the developers were able to find a way around it.
The heap is divided into different generations (or spaces) to achieve the best optimization possible. Each object on the heap is given a classification that allows for speeding up the collector’s processing.
The first space, Eden, is the first memory space where objects are created. You can think about it as the younger generation. When Eden is filled, certain objects are removed or moved to one of the other categories.
The Survivor space, also belonging to the young generation, is divided into survivor zero and survivor one. Finally, we have the Tenured space, where our program’s oldest and most essential objects are stored.
The collector checks the young generation memory slots on the heap very frequently. This is where the program’s expendable objects are stored.
The collector checks less frequently for objects we store in old-generation slots, such as Tenured. In this way, the operation and resources of the Garbage Collection process are much more efficient and less prone to errors.
Code Example and Explanation
An example of all of these methods at work can be seen here:
import java.util.ArrayList;
import java.util.List;
public class GarbageCollector {
private List<Object> eden;
private List<Object> survivor0;
private List<Object> survivor1;
private List<Object> tenured;
public GarbageCollector() {
eden = new ArrayList<>();
survivor0 = new ArrayList<>();
survivor1 = new ArrayList<>();
tenured = new ArrayList<>();
}
public void createObject() {
for (int i = 0; i < 1000000; i++) {
Object object = new Object();
eden.add(object);
if (i % 2 == 0) {
survivor0.add(object);
if (survivor0.size() >= 10000) {
promoteSurvivorObjects();
}
} else {
survivor1.add(object);
if (survivor1.size() >= 10000) {
promoteSurvivorObjects();
}
}
if (i % 100 == 0) {
tenured.add(object);
}
}
System.out.println("Eden Space Size: " + eden.size());
System.out.println("Survivor 0 Space Size: " + survivor0.size());
System.out.println("Survivor 1 Space Size: " + survivor1.size());
System.out.println("Tenured Space Size: " + tenured.size());
}
private void promoteSurvivorObjects() {
if (survivor0.size() >= 10000) {
tenured.addAll(survivor0);
survivor0.clear();
}
if (survivor1.size() >= 10000) {
tenured.addAll(survivor1);
survivor1.clear();
}
}
public static void main(String[] args) {
GarbageCollector gc = new GarbageCollector();
gc.createObject();
}
}

©History-Computer.com
Based on our previous example, we add a “max_objects” constant that defines the maximum number of objects to create. Then, the “survivor_threshold” specifies the point at which objects in that space should be moved into the tenured space.
We create objects in the “Eden” space. Then, based on our specified conditions, the objects are moved to the “survivor0” or “survivor1” spaces. In this case, if the index of our object is even, it is moved to “survivor0”, and if it’s odd, to “survivor1”. Then, if either of these lists exceeds the previously defined threshold, the “promoteSurvivorObjects” method is called, transporting objects to the “tenured” space.
It does so by repeatedly checking both bags, so to speak, to see if any of them have surpassed the threshold. Then the “main” method creates an instance of the “GarbageCollector” class, invoking the “createObject” method and demonstrating the functioning of the program.
Feel free to copy and paste this code into your console to analyze it more in-depth!
Different Types of Garbage Collectors
Depending on the characteristics of the program that we are developing, we can configure the garbage collector in four different ways. This is done by adding the respective JVM command line options to our code.
Serial Garbage Collector
A setup intended for simple, small-scale programs. It’s ideal for programs structured in a single thread. The only drawback is its stop-the-world feature, which stops the program from working once we initialize the Garbage Collection process.
To enable it, add the following option to the JVM command line: -XX:+UseSerialGC
Parallel Garbage Collector
Our second configuration option is used by the JVM as the default. It is similar to the first option but allows us to use multiple threads and CPUs to speed up the process. This is enabled by default, so you don’t need to add any extra lines.
Concurrent Mark-and-Sweep Collector (CMS)
The CMS is the first low-pause collector we come across. Low pause means that it rarely freezes an application’s threads, ensuring that the application continues to work.
The CMS is ideal for applications with a lot of threads and users. Its little predisposition to “stop the world” type events ensures continuity in the user experience. The collector runs at the same time as the rest of our application’s code — meaning it will consume more resources and processes.
Enable it by adding -XX:+UseConcMarkSweepGC before your garbage collector class.
Garbage First Garbage Collector
Also known as G1, this collector divides the heap into more search spaces than other collectors. Therefore, the G1 makes the selection of objects to be removed more thorough. It consumes more resources but rarely causes “stop the world” events.
Enable it with -XX:+UseG1GC and feel free to experiment with it!
Wrapping Up
Garbage Collection in Java is an essential concept for intermediate to advanced programmers. This process is incredibly beneficial for our code, allowing us to optimize our programs with just a few clicks.
The collector will take care of searching for and deleting those objects that we don’t use in our program. Therefore, we will be able to free up memory space and optimize the general performance of our software.
As Java programmers, we must take advantage of everything that this feature offers us. Not all programming languages offer automatic garbage collection, and performing this task manually can waste a lot of time.
Summary Table
Concept | Description |
---|---|
Garbage Collection in Java | An automatic process that removes unnecessary information from a program, optimizing its efficiency and loading times. |
Mark-And-Sweep Algorithm | The primary method used by the Garbage Collection tool. It marks objects that are no longer needed and then sweeps them away, freeing up memory space. |
Garbage Collection Optimization | The heap is divided into different generations (or spaces) to achieve the best optimization possible. Each object on the heap is given a classification that allows for speeding up the collectorâs processing. |
Types of Garbage Collectors | Depending on the characteristics of the program, the garbage collector can be configured in four different ways: Serial Garbage Collector, Parallel Garbage Collector, Concurrent Mark-and-Sweep Collector (CMS), and Garbage First Garbage Collector. |
The image featured at the top of this post is ©Panchenko Vladimir/Shutterstock.com.