Garbage Collection

The problem

In Java, when we create an object, it is stored in the Heap memory. All the objects in Heap are accessed through references. We can consider reference as a pointer to an object in Heap.

If a program is running in a finite amount of memory, from time to time we have to recover the memory by discarding unused objects so that we don’t run out of memory. If we reclaim the memory manually, few problems may happen –

  1. By mistake, we may discard an object even if that is in use.
  2. We may miss to discard an object even if that is no longer required.
  3. Difficult to achieve especially in case of concurrent programming.
The solution

Wouldn’t it be nice if someone performs this memory management job on our behalf? I would definitely say, yes. And the good news is, in Java, the Garbage Collector actually does that. Any object that is not in use will be discarded by the Garbage Collector automatically. There are few straightforward advantages –

  1. Simplified the coding by eliminating memory management overhead.
  2. Decouples memory management from business logic.
How objects become eligible for Garbage Collection?

An object becomes eligible for garbage collection when there are no live references to it. If no reference variable is referring to an object in Heap, that means that object becomes unreachable and you can’t get that object in any way. So that object becomes eligible for Garbage Collection (GC).

An object becomes unreachable due to many reasons. For example –

  1. Reference variable is declared within a method and that method is completed.
  2. Assign the reference variable to another object.
  3. Set null to the reference variable.

As per the first point – an object becomes eligible for garbage collection when that object is referred to by a reference variable that is declared within a method and that method finishes its execution. As we know a local variable lives only within the method that declared that variable. So, when the method execution is done, reference variable becomes dead.

public class OrdinaryClass {
    private void ordinaryMethod() {
        Object obj = new Object();
    }
}

In the example above, we are creating a new object and assigning that to a reference variable obj. As long as the method is running the reference variable obj will remain alive and so the object in heap.

Once the method execution is done, obj becomes dead and the object in heap will have no live reference and it becomes eligible for Garbage Collection.

In the second point we said – when we assign a reference variable to another object, the previous object becomes eligible for Garbage Collection. This is because as soon as the reference variable points to a new object, the old object is left behind by the reference variable and becomes unreachable.

Object obj = new Object();
obj = new Object();

In the third point we said – when we explicitly set null to a reference variable, you basically erase the link between the variable and the object. As a result, the object becomes eligible for Garbage Collection.

Object obj = new Object();
obj = null;
How Garbage Collection works?

Whenever we start a Java program, JVM creates a Garbage Collector daemon thread. Consider a daemon thread as a low priority thread that runs in the background. This Garbage Collector keeps track of all live objects present in the heap and discards them whenever they are no longer referred by the application.

We all know, objects reside in Heap space. This heap space is divided into two areas – Young generation and Old generation. The young generation consists of an area called Eden space along with two smaller survivor spaces S0 and S1.

When we create a new object, that object will be allocated to Eden Space. So, at some point, the Eden memory will be exhausted and that time a process called Minor Garbage Collection (GC) will run. This process will perform 2 tasks –

  1. Remove all dead objects from Eden.
  2. Move all live objects from Eden to S0 or S1 whichever is empty.

Initially both S0 and S1 will be empty. Let’s assume Minor GC will move live objects to S0. So after this step, both Eden and S1 will be empty and S0 will have some live objects.

After some time, Eden memory will be full again. Then again, Minor GC will run. This time all the dead objects from both Eden and S0 will be removed and live objects from both the areas will be moved to S1.

This process goes on like this. Please note, at any point of time, one of the survivor spaces is always empty and that empty survivor space is used to move live objects from Eden and the other survivor space.

You may ask, what if the empty survivor space is not big enough to accomodate all live objects? In that case, first, the live object will be moved to empty survivor space till it is full. Remaining objects will be directly copied to Old Generation.

Promotion

After each Minor GC, the live objects become old and their age increases. When that age reaches a certain threshold, they are promoted to the old generation from the young generation. This process is called as Promotion.

Major GC

Just like Minor GC cleans the Young Generation, Major GC cleans the Old Generation. Please note, the Old Generation is also known as the Tenured Generation.

Full GC

A Full GC is triggered whenever the heap space fills up. In that case the young generation is cleaned first followed by the old generation.

Can we request JVM for Garbage Collection?

Garbage collection happens automatically. However, we can call System.gc() and Runtime.getRuntime().gc() to request the JVM for garbage collection. This call triggers a Major GC. But there is no guarantee that the actual Garbage Collection will be triggered. Both System.gc() and Runtime.getRuntime().gc() are effectively the same as System.gc() internally invokes Runtime.getRuntime().gc().

public final class System {
    public static void gc() {
        Runtime.getRuntime().gc();
    }
}

Please try to avoid this explicit garbage collection invocation.

The finalize() method

Finalize method is used to perform cleanup activity before destroying any object. It is called by the Garbage collector before removing the object from memory. This method is present in Object class and by default it doesn’t do anything.

public class Object {
    protected void finalize() throws Throwable { }
}

If you want to perform some cleanup activity before an object is destroyed, you have to override this method.

public class OrdinaryClass {
    public static void main(String[] args) throws Exception {
        OrdinaryClass obj = new OrdinaryClass();
        obj = null;   
        System.gc(); // call garbage collector  
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("Performing cleanup activity");
    }
}

Output:

Performing cleanup activity

In the example above, we first created an object. Then we have assigned null to the reference variable obj to make the object eligible for Garbage Collection. Finally we explicitly requested JVM for Garbage Collection. During Garbage collection, the object is destroyed. But before that the finalize() method is called.

That’s it for now. Hope you have enjoyed this tutorial. If you have any doubt, please ask in the comment section. I will try to answer that as soon as possible. Till then, bye bye.