Java Garbage Collector — A memory management solution

Flavius Zichil
Javarevisited
Published in
7 min read2 days ago
Photo by Jonas Svidras on Unsplash

When writing a Java program, many variables are used to store the state of the program during its execution. Some of them are needed during the entire execution, while others have only temporary purposes. Either way, to store the variables’ values, Java saves them on the machine’s physical memory where the program runs. Because the physical memory is limited, a mechanism for removing the unused variables from memory is required. In Java, this mechanism is called Garbage Collector.

The Garbage Collector is a built-in process in Java that searches in the Heap Memory for objects that are not referenced anymore or are out of scope to free up space for other variables to be stored there. It runs automatically and is handled by JVM (Java Virtual Machine).

Ok, wait a second! What is this Heap Memory? And what does it mean for a variable to be out of scope? What is a reference to an object? To better understand how the Garbage Collector works, let’s begin by discussing these aspects.

Stack vs Heap memory

Before running a Java program, the JVM allocates two types of memory for it: Stack Memory and Heap Memory.

The Stack Memory stores information like the order of method execution, local variables, or object references. If no more space exists in this memory, the program will throw java.lang.StackOverFlowError which stops the program execution. Fortunately, the size of this memory can be programmatically increased using the -Xss argument of JVM.

The Heap Memory stores the objects created during the program’s execution. When the capacity of this memory is exceeded, a java.lang.OutOfMemoryError will be thrown, also stopping the execution of the program. Like the Stack Memory, its size is also configurable using the following JVM arguments:

  • -Xmx — maximum Heap Memory size
  • -Xms — initial Heap Memory size

The Garbage Collector only operates inside the Heap Memory, so it only removes objects used by Java programs.

But what about object references? Isn’t a reference and an object the same thing? This topic creates confusion between Java developers quite often. Let’s clarify it!

Reference vs Object

A reference is a fixed-size variable identified by a name and refers to an object. All the references are stored in the Stack Memory. On the other hand, an object can have different sizes and is stored in the Heap Memory. Its content is only accessible through a reference. While it is possible to assign a reference to another, it‘s not possible for objects.

You can also think of it this way: the reference acts as the interface visible in the code, while the object serves as the implementation of that interface and remains hidden from the developer.

The objects are always stored in the Heap Memory and are only accessible through a reference. The references are stored in the Stack Memory and refer to an object.

When it comes to the Garbage Collector, it only removes the objects from the Heap Memory, not the references. At this moment we know where the Garbage Collector operates and what it can remove to free up space. What we don’t know yet is when an object is getting out of scope so the Garbage Collector can remove it from memory.

Variable scope

In Java, there are multiple types of variables, each of them having its scope. When discussing scope, we can also think about it as how much time is the variable needed by the program. From the Garbage Collector’s point of view, it is crucial to identify when a variable is out of scope because, at that point, it can be removed from memory.

There are four types of variables in Java:

  • local variable — are losing their scope after the block in which they are declared is finished
  • method parameter — are losing their scope when the method is finished
  • instance variable — are losing their scope when the object has no references left
  • class variable — are losing their scope only when the program ends

The local variables and method parameters are being collected more often by the Garbage Collector because their scope is only temporary. There would have been no point in keeping these objects in memory during the entire program execution since it is obvious that they won’t be used anymore after the block or method ends.

How does the Garbage Collector work?

Now that we have all the necessary knowledge, we can proceed with understanding the Garbage Collector’s mechanism. Let’s start by looking at the following code snippet:

public static void main(String[] args) {
String firstName = new String("John");
String middleName = new String("Eddie");
String lastNAme = new String("Doe");

middleName = firstName;
lastName = middleName;
}

Let’s analyze each line of code to understand how the Garbage Collector sees it. Initially, a new String with the value “John” is assigned to the firstName variable.

String firstName = new String("John");

Then, a new String is created with the value “Eddie”.

String middleName = new String("Eddie");

Next, another String with the value “Doe” is created and assigned to the lastName variable.

String lastNAme = new String("Doe");

So far so good. Because each String was created using the new keyword, there is a different object in the Heap Memory for each of them (Off topic: even if two Strings were created with the same value, three different objects would still be created because of the new keyword. Java offers a String Pool to avoid this behavior).

In the next statement, the firstName reference is assigned to the middleName reference. Now, the middleName refers to the object that firstName refers to. Remember from the previous section that only the reference is assigned to another reference, not the objects they refer to.

middleName = firstName;

What is happening is that the object with the value “Eddie” has no references left, thus becoming available for the Garbage Collector. Because an object can only be identified through a reference, after losing its only reference there’s no way to reuse it, so it can be removed from the memory. But it is not removed at this point. It is only marked as ready for garbage collection.

In the last statement, the middleName reference is assigned to the lastName reference. Since the middleName reference already refers to “John”, so will the lastName reference.

lastName = middleName;

At this point, object “Doe” has also no references pointing to it, so it becomes available for garbage collection. Since an object becomes ready for being removed from the memory if it has no more references or it gets out of scope, the “John” object will only become available for the Garbage Collector when the main method ends because that is the moment it gets out of scope.

But when does the Garbage Collector run if not when the object becomes collectable? It runs periodically and is independent, allowing the developer to focus on the actual job instead of taking care of memory deallocation. It can also be programmatically executed using System.gc(), but there is no guarantee that it will run exactly when the method is called. So, there’s not much a developer can do when it comes to changing Garbage Collector behavior.

Memory leaks

But, there is something the developer can do to help the Garbage Collector: to avoid writing code that may generate memory leaks. But what is a memory leak?

A memory leak is a section of the memory that cannot be reallocated by the Garbage Collector because it is still referenced even if it’s not used anymore by the program.

Two of the most common ways you, as a developer, can avoid memory leaks are:

  • don’t leave Resouces unclosed — using resources like input streams or database connections and not closing them when not used anymore will make them impossible to remove from the memory
  • limit the number of static fields — the static fields are class variables, so their scope only ends when the program ends too. Having too many static fields will keep a large section of the Heap Memory blocked during the entire program execution.

Conclusion

The Garbage Collector is a complex mechanism. It is built-in in Java, so there’s not much a developer can do to change its behavior. But the Garbage Collector isn’t perfect and here’s where the memory leaks appear. It is your duty, as a developer, to help the Garbage Colletor be perfect by writing memory-leak-proof code.

Understanding core Java concepts like variable scopes, the difference between references and objects, and different memory types creates the possibility of writing better code thus creating a perfect symbiotic relationship with the Garbage Collector.

Thank you for reading this!

--

--

Flavius Zichil
Javarevisited

Full-Stack Developer, working with Java (Spring), Angular, Jenkins and many other related technologies.