JavaData & JavaAdvanced Concepts of Java Garbage Collection

Advanced Concepts of Java Garbage Collection

Garbage collection (GC) is a background process run by JVM (Java Virtual Machine) to housekeep memory automatically when a Java application runs in the foreground. The presence of a garbage collector relieves the programmer of the responsibility of writing an explicit memory deallocation routine in every application they develop. This leverages productivity while coding. A programmer can focus exclusively on solving the problem at hand and let JVM handle memory management issues. Garbage collection is a complicated procedure in its own right. Once you dive deeper into the arena, you realize that there is more to it than meets the eye. This article is an attempt to explore some of those areas along with the APIs related to garbage collection.

Explicit Versus Implicit GC

Reclaiming unused memory is a complex procedure and doing it explicitly through code can be error-prone, leading to unexpected behavior of the program. A couple of the problems can be as follows:

  • Dangling Reference: Suppose object A refers to object B and B refers to object C, and in due course, object C is deallocated and some other object has been newly allocated to that space. Any attempt to refer to objects C through A may result in unpredictable behavior. This is a common problem with C/C++ pointers.
  • Memory Leaks: This occurs when an allocated memory segment, though not referenced, is yet not released. Imagine deleting the root element of a tree; the leaf nodes, though, occupy memory yet they reaches an unreachable state. These spaces keep consuming resources yet can neither be used or reclaimed, causing memory leaks.

Memory management in Java takes an alternative approach. Like most modern object-oriented languages, it uses an automatic memory manager called a garbage collector. Garbage collection has three primary functions:

  • Memory allocation
  • Ensure that only a referenced object remains in memory
  • Reclaims memory from the clutches of unreachable references

GC Design

Java memory manager segments memory into three categories: young generation, old generation, and permanent generation. Before going further, imagine effacing memory by GC to occur in waves. Fresh, new objects are allocated in the young generation. The old generation contains those objects that have stayed in the young generation for some time; also, some large objects are directly allocated in the old generation segment. Permanent generation objects are those that GC finds easy to manage, such as objects that describe classes and methods. Young generation segments contain an area called Eden and two smaller Survivor Spaces. Eden contains those objects that have survived at least one garbage collection wave and given an opportunity to die before being moved to Survivor Spaces and ultimately to old generation status. Typically, when the young generation is filled up, a minor collection (an algorithm) wave pops up and either and does the cleaning or objects are moved to the next status. When the old generation is filled up, the major collection (another algorithm) wave does the job, which means practically all generations are collected/cleaned.

This is a rudimentary idea behind GC design. Refer to “Memory Management in the Java HotSpotTM Virtual Machine, Sun Microsystems” for a detailed explanation.

GC Algorithms

There are four Garbage Collection (GC) algorithm available in Java Hotspot VM. Let’s get an idea of each of them in a line or two.

  • Serial GC: This algorithm collects both young and old generations serially. Application execution is paused while collection takes place.
  • Parallel GC: This is the parallel version of the previous algorithm, particularly useful for applications that run on a machine with a large memory base and multiple CPUs.
  • Mostly Concurrent Mark and Sweep (CMS) GC: This algorithm avoids the problem of long pauses by not compacting old generation items but using free-lists to manage reclaimed space and mainly collecting in the mark and sweep phase in parallel while the application executes.
  • Garbage First GC: This collection algorithm, a replacement of CMS, brings a sense of predictability and configurability of the so-called unpredictable collection time. That means you can request the pause time to be of no longer that x amount of time, though there is no certainty that the request will be granted.

Refer to Java Garbage Collection by N. Salnikov-Tarnovski and G. Smirnov for a detail analysis on each of the algorithm.

The gc() Method

JVM runs the garbage collector as soon as the system is low on memory. Can we invoke garbage collector from our code? Yes, but there is no guarantee that garbage collector would listen. The gc() method in java.lang.Runtime can be used to “suggest” that JVM run garbage collector, which it may totally ignore, so no point in being sentimental here. The Java API Documentation states the method gc() as follows…

“Runs the garbage collector. Calling this method suggests that the Java virtual machine expends effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the virtual machine has made its best effort to recycle all discarded objects.

The name gc stands for “garbage collector.” The virtual machine performs this recycling process automatically as needed, in a separate thread, even if the gc method is not invoked explicitly.

The method System.gc() is the conventional and convenient means of invoking this method.”

package org.mano.example;

public class Main {

   public static void main(String[] args) {

      Runtime runtime = Runtime.getRuntime();

      Object[] obj = new Object[500];
      for (int j = 0; j < 5; j++) {
         System.out.println("Free Memory="
         + runtime.freeMemory());
         for (int i = 0; i < 500; i++) {
            obj[i] = new Object();
         }
         System.out.println("Iteration: "+j
         + " Free Memory is " + runtime.freeMemory());
         System.gc();
         System.out.println("gc called, free Memory now is "
         + runtime.freeMemory());
         System.out.println("--------------------------");

      }
   }
}

Output: (may vary in your case)

Free Memory=61740128
Iteration: 0 Free Memory is 61740128
gc explicitly called free Memory now is 61790856
------------------------------------------------------
Free Memory=61790856
Iteration: 1 Free Memory is 61790856
gc  explicitly called free Memory now is 61791272
------------------------------------------------------
Free Memory=61791272
Iteration: 2 Free Memory is 61791272
gc explicitly called free Memory now is 61791272
------------------------------------------------------
Free Memory=61791272
Iteration: 3 Free Memory is 61791272
gc explicitly called free Memory now is 61791272
------------------------------------------------------
Free Memory=61791272
Iteration: 4 Free Memory is 61791272
gc explicitly called free Memory now is 61791272
------------------------------------------------------

Observe the change in memory size after gc() is called.

Finally, Finalize…

The invocation to finalize() a method states a set of actions to be performed on the object just before garbage collection reclaims its used memory location. The method finalize() is a member of thee Object class which is the parent class of all classes in Java. Also, it is declared as protected; that means any class can override this method.

package org.mano.example;

public class Main {

   private String 

   public Main(String name) {
      this.name = name;
   }

   @Override
   public void finalize() {
      system.out.println("destiny calls.
      You'll be wiped out, "
      + name);
   }

   public static void main(String[] args) {
      for (int i = 0; i < 5; i++) {
         new Main("A" + i);
      }
      System.gc();
   }
}

Objects, when they go out of scope, are marked for finalization and placed on the queue before actual garbage collector reclaims the memory. If, however, you want to finalize all objects waiting to be finalized by Java run-time, you may use the runFnialization() method declared as a member of the Runtime class as well as the System class. It may be invoked either as:

Runtime.getRuntime().runFinalization();

or

System.runFinalization();

An interesting aspect is that there is a quanta of time available between the object being marked for finalization and the object being actually effaced. Garbage collector comes as the next phase after finalization and checks again if the object to be effaced is still out of scope. Though not a good idea, we can try to resurrect the object between the invocation of the finalize() method and actual garbage collection as follows:

package org.mano.example;

public class Main {
   privateString name;

   // ...same as above

   @Override
   public void finalize() {
       name="I am back!!";
      System.out.println("destiny calls.
      You'll be wiped out, "
      + name);
   }

   // ...same  as above
}

This is a very bad idea and a bad programming practice as well. No harm in experimenting, though, yet never do it in real life.

The finalize() method is discouraged to be used explicitly because this method is automatically called by the GC to perform a cleanup operation on an object just before GC reclaims the object’s memory. GC does not guarantee any specific time of execution. It may never execute before the program terminates; thus, it is highly improbable if or when the method finalize() would be called.

Finalize, Finally…

As it was said earlier, Java does not give exclusive control over the time when garbage collector will execute. Every method related to garbage collection is just a suggestion to JVM that it may reclaim the memory now. Similarly, the try…finally clause simply states the release of resources used in the try block. The finally block is guaranteed to execute. This ensures that the garbage collector can reclaim the memory used by the object of this class.

try{
   // resources used
}finally{
   // resources released
}

Reference Type

Java offer different types of references that can be used to designate a reference object class to give a new meaning. A program may use one of these type of reference object that refers to some other object in such a manner that the objects get collected by GC exclusively, depending upon its reference type. If you are not aware of any reference types in Java, that means you have been using only strong reference types. It is ordinary reference types, such as:

String greet=new String("Hello");

Apart the from strong reference, there are three other type of reference—namely, soft, weak, and phantom—denoted by the classes SoftReference<T>, WeakReference<T>, and PhantomReference<T> defined in the java.lang.ref package. The reference object type implemented by these types are all subclasses of the abstract base class called Reference<T>.

Soft references are for implementing memory-sensitive caches.”

Weak references are for implementing canonicalizing mappings that do not prevent their keys (or values) from being reclaimed.”

Phantom references are for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.”

Each of these types corresponds to a different level of reachability. According to Java API Documentation,

  • An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly created object is strongly reachable by the thread that created it.
  • An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
  • An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly reachable object are cleared, the object becomes eligible for finalization.
  • An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
  • Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways.

A complete explanation of reference types require a exclusive focus on it. Let’s set it aside for now.

Conclusion

Garbage collection is a complex subsystem under the aegis of JVM where the most crucial managerial process is taken care of. It may not be perfect, but nonetheless gives a sense of freedom to the programmer from a critical responsibility. This article is an attempt to surface the intricate story behind GC on how it works. Above all, this will give you another reason to appreciate and thank all who shaped GC in its current form. GC is undoubtedly a reason to make Java a prime language, with many improvements yet to be implemented to still prod on.…

References

  • N. Salnikov-Tarnovski, G. Smirnov, Java Garbage Collection, PDF
  • Memory Management in the Java HotSpotTM Virtual Machine, Sun Microsystems
  • Java API Documentation
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories