
Vladimir Panchenko / Shutterstock.com
Garbage collection in Java is a sophisticated programming concept that is critical for producing efficient code. The Garbage Collection tool helps you to delete superfluous information from your software with a few clicks using an automatic method.
While the procedure itself is simple to operate, the notion behind it is complex and is governed by a number of variables. There’s a lot to learn here, so let’s get started right away.
Fundamentals of Java Garbage Collection
In Java, garbage collection is an automatic procedure that removes information or code that we no longer require.
This operation ensures that any software will run as efficiently as possible and with the quickest loading times possible. Typically, the procedure is run on software that use a Java Virtual Machine (JVM).
Other languages, such as C++ and C, require manual garbage collection, which requires paying close attention to which items are removed. Because this process can be difficult and time-consuming, automation is a huge benefit.
JVM programs have a designated area of memory called the heap that uses dynamic allocation. The Garbage Collection tool constantly communicates with the heap and determines which objects should be deleted automatically. This means that once the program is started, we have no control over it.
However, before executing the Garbage Collector, we may give a number of options to indicate what type of objects we want to remove and when.
But why should we bother with Garbage Collection?
To begin with, a program that does not routinely eliminate unwanted items from its heap will have storage issues. However, programs that do not use Garbage Collection tools will be under-optimized, resulting in slower and less efficient processes.
Garbage Collection: How Does It Work?
The task is straightforward: we must determine which objects are no longer required for the program’s correct operation and then eliminate them. But how does the collector recognize these objects?
When we execute the garbage collector for the first time, it begins by identifying the data links between objects and other code components. If an object isn’t referenced from somewhere else, the collector deletes it and frees the memory space it was using.
To accomplish this work, the Garbage Collection tool primarily use the mark-and-sweep method. This approach is explained in detail below, as understanding it is critical to avoiding common problems such as memory leaks.
Mark-And-Sweep Algorithm
The Garbage Collection procedure is organized around the mark-and-sweep algorithm. This algorithm, as the name suggests, consists of two phases: Mark and Sweep.
We initialize the mark-and-sweep algorithm whenever we create an object in the heap. In the first instance, the trash collector investigates an object’s tree, beginning at its roots.
If the object is referenced in the roots, the mark (which was initially set to 0) changes to 1 (true).
In all other instances, the mark’s value remains 0. This signifies that the object is not referenced in any other portions of the code. The collector destroys all objects with mark 0 (false) and frees memory space during the sweep phase.
The following is a simplified mark-and-sweep algorithm:
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
The runGarbageCollection method is used here to initialize the GarbageCollector class, which saves all objects in two lists: roots and objects. The addObject method creates heap objects with which the markPhase and sweepPhase methods can work.
As previously stated, the markPhase method marks all reachable objects by placing them at the end of the objects list. The sweepPhase method then iterates through the list, identifying items with marks based on whether or not they are reachable.
If they are not, they are removed from the list. Finally, the main method shows all of this by performing the entire process and outputting the objects that remain once the algorithm has completed its work.
Garbage Collection Optimization
Garbage Collection in Java, like any other programming tool, can become clogged. This leads in a subpar experience, but thankfully, the developers were able to work around it.
To obtain the optimum optimization, the heap is separated into different generations (or spaces). Each object on the heap is classified in order to speed up the collector’s processing.
Eden is the first memory area in which items are created. Consider it in terms of the younger generation. When Eden is full, certain artifacts are deleted or relocated to another category.
TheSurvivorspace, which is also from the younger generation, is separated into survivor zero and survivor one. Finally, there is the Tenured space, which houses our program’s oldest and most important things.
The collector constantly scans the heap’s young generation memory spaces. This is the location of the program’s throwaway items.
The collector looks for objects in old-generation slots, such as Tenured, less frequently. As a result, the operation and resources of the Garbage Collection process are significantly more efficient and error-free.
Code Example and Explanation
Here’s an example of all of these methods in action:
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 the preceding example, we add a max_objects constant that specifies the maximum number of objects that can be created. The survivor_threshold then determines when objects in that space should be moved into the tenured space.
In the Eden space, we make items. The objects are then relocated to the survivor0 or survivor1 slots based on our defined conditions. If the index of our item is even, it is moved to survivor0, and if it is odd, it is moved to survivor1. The promoteSurvivorObjects method is then executed if one of these lists exceeds the previously set threshold, moving items to the tenured space.
It accomplishes this by repeatedly monitoring both bags to see if any of them have exceeded the threshold. The main method then generates an instance of the GarbageCollector class, running the createObject method and displaying the program’s functionality.
Copy and paste this code into your terminal to investigate it further!
Different Types of Garbage Collectors
We can set the trash collector in four different ways depending on the specifics of the software that we are constructing. This is accomplished by including the appropriate JVM command line parameters in our code.
Serial Garbage Collector
A configuration designed for basic, small-scale projects. It is appropriate for programs with a single thread. The only disadvantage is that it has a stop-the-world feature that prevents the program from running once we start the Garbage Collection process.
Add the following option to the JVM command line to activate it: -XX:+UseSerialGC
Parallel Garbage Collector
The JVM uses our second configuration choice as the default. It is similar to the first alternative, but it allows us to speed up the operation by using several threads and CPUs. This is enabled by default, therefore no more lines are required.
Concurrent Mark-and-Sweep Collector (CMS)
The CMS is the first low-pause collector that we encounter. Low pause means that it rarely freezes a program’s threads, allowing the application to continue to function.
The CMS is best suited for applications with a high number of threads and users. Its low proclivity to stop the global events maintains consistency in the customer experience. The collector runs concurrently with the rest of our application’s code, which means it consumes additional resources and processes.
Add -XX:+UseConcMarkSweepGC before your garbage collector class to enable it.
Garbage First Garbage Collector
This collector, often known as G1, separates the heap into more search spaces than previous collectors. As a result, the G1 refines the selection of objects to be deleted. It requires more resources but rarely brings global events to a halt.
Enable it by typing -XX:+UseG1GC.and feel free to play around with it!
Wrapping Up
Garbage collection is an important concept in Java for intermediate to advanced programmers. This procedure is really advantageous to our code because it allows us to optimize our applications with a few clicks.
The collector will search for and delete any items that we do not need in our program. As a result, we will be able to free up RAM and improve the overall performance of our applications.
As Java programmers, we must take use of all that this feature has to offer. Not all programming languages have automatic garbage collection, and doing so manually might be time-consuming.