underRated star: Garbage Collector

In Java, when you run a program, objects will be created, probably using our favorite ‘new’ operator, JVM’s heap stores all these objects. The memory for these new objects is allocated on the heap at run time. Explicit memory management (allocating memory, freeing memory, and keeping track of what memory can be freed when etc) has proved to be a source of bugs, crashes, memory leaks, and poor performance in other programming languages like C/C++.

Here comes Garbage Collector (GC) to the rescue: GC is a thread, which gets started by JVM, runs in the background, and takes care of the ‘garbage’ (objects that are no longer needed by the program)..  thereby freeing up memory J.

[Automatic garbage collection is an integral part of Java and its run-time system.]

In addition to freeing unreferenced objects, a garbage collector may also combat heap fragmentation. When an object is garbage collected and the memory is freed, it might cause empty space in memory between blocks occupied by live objects. After a while, there might be enough unused space in memory but not enough ‘contiguous’ free heap space available. Don’t worry; this is also taken care by GC.

Remember!, The JVM controls the Garbage Collector; it decides when to run the Garbage Collector. Ideally JVM should definitely run GC when it realizes that the memory is running low, but even this behavior can not be guaranteed. One can request the Garbage Collection to happen from within the java program (shown below) but again there is no guarantee that this request will be taken care of by JVM.

Runtime runtime = Runtime.getRuntime().gc();         OR                  System.gc();

An object may have a finalizer: a method that the garbage collector must run on the object prior to freeing the object. To add a finalizer to a class, you simply declare a method in that class as follows:

protected void finalize() throws Throwable {
        //...
        super.finalize();
}

If an exception is thrown during the execution of the finalize() method, the exception will be ignored and the garbage collection (finalization) of that object terminates.

Note: You may enable/disable call of finalize() method on the exit of the application using  Runtime.getRuntime().runFinalizersOnExit(boolean value) .

So First, the garbage collector must in some way detect unreferenced objects. (Step 1)

A garbage collector must then examine all objects it has discovered to be unreferenced to see if any include a finalize() method. (Step 2)

After executing all finalizers, the garbage collector must once again detect unreferenced objects starting with the root nodes. This step is needed because finalizers can “resurrect” unreferenced objects and make them referenced again. (Step 3)

Ok you will understand how to GC pulls of these 3 steps once you have idea about how the GC classifies the objects on the heap..

Reachability lifecycle of Objects

Every object on the heap is in one of three states: reachable, resurrectable, or unreachable.

Reachable: A live thread holds reference to this object. In JVMs terms, the object can be ‘reached’ by tracing out the graph of object references starting with the root nodes. Every object begins its life in the reachable state, and stays reachable so long as the program maintains at least one reachable reference to the object. As soon as the program releases all references to an object, however, the object becomes ‘resurrectable’.

Resurrectable: An object is in the resurrectable state if it is not currently reachable by tracing the graph of references starting with the root nodes, but could potentially be made reachable again later when the garbage collector executes some finalizer. All objects, not just objects that declare a finalize() method, pass through the resurrectable state. As mentioned in the previous section, the finalizer for an object may “resurrect” itself or any other resurrectable object by making the objects reachable again. Because any object in the resurrectable state could potentially be made reachable again by its own or some other object’s finalize() method, the garbage collector cannot reclaim the memory occupied by a resurrectable object before it makes certain the object won’t be brought back to life through the execution of a finalizer. By running the finalizers of all resurrectable objects that declare a finalize() method, the garbage collector will transform the state of all resurrectable objects, either back to the reachable state (for objects that get resurrected), or forward to the unreachable state.

Unreachable: The unreachable state indicates not only that an object is no longer reachable, but also that the object cannot be made reachable again through the execution of some finalizer. Unreachable objects can no longer have any affect on the running program. The garbage collector, therefore, is free to reclaim the memory they occupy.

In 1.2 version of Java, the Reachable type of objects were further classified..

Strongly Reachable: These are the objects we use on daily basis so there is nothing new here. Any object referenced directly from a root node, such as a local variable, is strongly reachable. Likewise, any object referenced directly from an instance variable of a strongly reachable object is strongly reachable.

New part is the ‘Progressively weak reachability’ introduced in 1.2, these new states are called as the: softly, weakly, and phantom reachable.

Reference Objects

The purpose of reference objects is to enable you to hold references to objects that the garbage collector is free to collect. A reference object encapsulates a reference to some other object, called the referent. All reference objects are instances of subclasses of the abstract java.lang.ref.Reference class. The family of Reference classes, includes three direct subclasses: SoftReference, WeakReference, and PhantomReference.

The fundamental difference between a strong reference and its three progressively weaker cousins – is that a strong reference prevents its referent from being garbage collected, whereas soft, weak, and phantom references do not. To create a soft, weak, or phantom reference, you simply pass a strong reference to the constructor of the appropriate type of reference object.

WeakReference<Dog> wr  = new WeakReference<Dog>(new Dog(), rq);

Once a reference object is created, it will continue to hold its soft, weak or phantom reference to its referent until it is cleared by the program or the garbage collector. To clear a reference object, the program or garbage collector need only invoke clear(), a method defined in class Reference, on the reference object. Clearing a reference object invalidates the soft, weak, or phantom reference contained in the reference object. For example, if the program or garbage collector were to invoke clear() on the WeakReference object, the weak reference to the Dog object would be invalidated, and the Dog object would no longer be weakly reachable as it has been cleared and marked as ‘ressurectable’.

It is often important to keep track of reachability state changes brought about by the garbage collector to your weaker references. You can register to be notified when such changes occur. To register interest in reachability state changes i.e. when the object is cleared, you associate reference objects with reference queues. A reference queue is an instance of class java.lang.ref.ReferenceQueue to which the garbage collector will append (or “enqueue”) reference objects involved in reachability state changes asynchronously by the garbage collector.

To associate a reference object with a reference queue, you simply pass a reference to the reference queue as a constructor parameter when you create the reference object.

ReferenceQueue<Dog> rq = new ReferenceQueue<Dog>(); //1

WeakReference<Dog> wr  = new WeakReference<Dog>(new Dog(), rq); //2

When the garbage collector makes a relevant change to the reachability state of the referent, it will append the reference object to its associated reference queue. For example, when the WeakReference object was created, two references were passed to the constructor: dog object and a reference to a ReferenceQueue object. When the garbage collector decides to collect the weakly reachable Dog object, it will clear the WeakReference object and append the WeakReference object to its reference queue.

Let me show you a demo:

import java.lang.ref.*;

public class TestWeakReference {
      public static void main(String[] args) {
             ReferenceQueue<Dog> rq = new ReferenceQueue<Dog>(); //1

             //The WeakReference object is strongly referenced from a local variable,
             //which, like all local variables, serves as a root node for the garbage collector.
             WeakReference<Dog> wr  = new WeakReference<Dog>(new Dog(), rq); //2
             //This is how you get the dog when you want it.
             System.out.println("wr.get() : " + wr.get()); //3

             Reference<? extends Dog> ref = null; //4
             //Keeps running till rq.poll returns something
             for(ref = rq.poll(); ref==null; ref = rq.poll()){ //5
                            System.out.println("Waiting for GC to clear Dog"); //6
             } //7
             System.out.println("ref : " + ref); //8

             //GC has cleared the dog, marked it as ‘resurrectable’, so you will not get anything here
             System.out.println("wr.get() : " + wr.get()); //9
             //Again, you will not get anything here aswell
             System.out.println("ref.get() : " + ref.get()); //10
       }
}
class Dog{
      @Override
      protected void finalize() throws Throwable {
              //GC will now mark the dog object as ‘unreachable’ and claim its memory
              System.out.println("I'm GC and I'm destroying dog");
              super.finalize();
      }

      @Override
      public String toString() {
              return "I'm a Dog";
      }
}

Output:

wr.get() : I’m a Do

Waiting for GC to clear Dog
Waiting for GC to clear Dog
Waiting for GC to clear Dog
..
Waiting for GC to clear Dog
Waiting for GC to clear Dog
Waiting for GC to clear Dog 
ref : java.lang.ref.WeakReference@9304b1
ref.get() : null
wr.get() : null
I'm GC and I'm destroying dog
  • softly reachable – Using a SoftReference tells the garbage collector that you would like it to keep the object around for a while, until memory considerations cause it to reclaim the object. To be more technical, they can be reached from the roots via one or more (uncleared) soft reference objects. The garbage collector may reclaim the memory occupied by a softly reachable object. If it does so, it clears all soft references to that softly reachable object. When the garbage collector clears a soft reference object that is associated with a reference queue, it enqueues that soft reference object. SoftReferences are primarily for implementing memory-senstive caches.
  • weakly reachable – Using a WeakReference tells the garbage collector that there’s no reason to keep the object around any longer than necessary. Hence they are cleared aggressively. To be more technical, they can be reached from the roots via one or more (uncleared) weak reference objects. The garbage collector must reclaim the memory occupied by a weakly reachable object. When it does so, it clears all the weak references to that weakly reachable object. When the garbage collector clears a weak reference object that is associated with a reference queue, it enqueues that weak reference object. WeakReferences are primarily for associating extra information with an object (that’s what WeakHashMap does).
  • phantom reachable – They are not strongly, softly, nor weakly reachable, and have been determined to not be resurrectable by any finalizer (if it declares a finalize() method itself, then its finalizer will have been run), and is reachable only from the roots via one or more (uncleared) phantom reference objects. As soon as an object referenced by a phantom reference object becomes phantom reachable, the garbage collector will enqueue it. The garbage collector will never clear a phantom reference. All phantom references must be explicitly cleared by the program.

Note: The garbage collector enqueues soft reference objects to indicate their referents have just left the softly reachable state.

Likewise, the garbage collector enqueues weak reference objects to indicate their referents have just left the weakly reachable state.

But the garbage collector enqueues phantom reference objects to indicate their referents have entered the phantom reachable state. Phantom reachable objects will remain phantom reachable until their reference objects are explicitly cleared by the program.

What is WeakHashMap for?

It’s not for caching. The idea is, suppose you have a bunch of objects of a certain class that you can’t extend, but you want to associate some other piece of information with each object. You can use a Map, with the main object as the key and the extra info as the value. Using a WeakHashMap for this will make sure that your Map won’t cause a memory leak, because it won’t hold a strong reference to the main (key) object; this will allow the object to be garbage collected when it’s no longer needed. I believe that when the key is garbage collected, the value will soon be garbage collected too, though not immediately.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s