Monday, August 16, 2010

Java References

We all have known the concept of a "reference" in Java. It is one of the basics of the language. Any object in java is reached by a reference. Reference is the lifeline of an object; without the reference there is no chance for an object to live after a visit by the garbage collector. The references that we create are generally called as 'strong references'. It makes an object persist in the java heap(memory which holds all java objects in a JVM) as long as the reference is reachable even though the object is not used for the rest of the code. Most of the applications now use a lot of objects which include multimedia and other memory consuming objects. It would be really nice if we could say to the VM that the big picture that it is showing can be removed from memory to make room for a more important document instead of crashing by throwing an OutOfMemoryError. As of Java 2(Yes! Its been around here for a while before I came to know of it), we have three additional reference types that cater our needs regarding the object lifetime. They are a way to interact with the Garbage Collector.

They are:
1. SoftReference,
2. WeakReference,
3. PhantomReference.
from the package java.lang.ref ordered by their strictness of the object hold.

A Softly Referenced object cannot have a strong reference but a SoftReference pointing at it.

A Weakly Referenced object cannot have a strong reference, Soft reference but a WeakReference pointing at it.

A Phantomly Referenced object cannot have a strong reference, soft reference, weak reference but a PhantomReference pointing at it.

We always should use the get() method of the Reference class to get the reference of the object that it points to.

SoftReference:

A softly referenced object stays in memory as long as the garbage collector feels there is enough memory to keep it alive (Note that the garbage collector may collect this object even if there is enough memory). A sample code is given below:

Date d = new Date();
SoftReference sr = new SoftReference(d);
d = null; //remove the strong reference
System.out.println(sr.get()); //prints the date provided no GC is run after line 2
System.gc();
System.out.println(sr.get()); //may or may not print the date


This makes the soft reference an ideal candidate to store objects like pictures alive as long as memory permits.

WeakReference:

A Weakly referenced object is garbage collected as soon as the GC finds it. A sample code is given below:

Date d = new Date();
WeakReference sr = new WeakReference(d);
d = null; //remove the strong reference
System.out.println(sr.get()); //prints the date provided no GC is run after line 2
System.gc();
System.out.println(sr.get()); //Will print only null


A weak reference is mostly used for mappings. In other words, the weak reference acts as a key to a value in a map structure. Usually the key is a kind of object that has a small lifetime so that it is acceptable that it can be Collected by GC as soon as possible. So collecting the key is ok. How do we remove the value that this key is pointing to? We have a ReferenceQueue data structure which will help us keep track of all the references of GC collected objects. We will see about this queue at the end. Just keep in mind that this queue will contain the references.

PhantomReference:

This has the simplest definition of all the references but the most annoying one for me to understand. I took a lot of time to understand its use and i could finally get some help. If I am not to the point here, you are welcome to correct me :-)
When an object is referred by a phantom reference it means that there is no way that we could get a reference to the original object(Except by using Reflection). The get() method of the PhantomReference class always returns null even though the original object is still around. This is designed to avoid the user from creating a strong reference to the referred object. With the other two references it is possible to create a strong reference to the referent. But with a PhantomReference you can't. That is because the finalization on that object is already done and you are not supposed do any operations further on that object. Then whats the purpose of such an object? The only purpose is to know, when an object is ready for removal. At that point of time, the reference is added to the ReferenceQueue. Huh! Why would you even want to know that? Because when the referent becomes phantomly reachable, you may subsequently want to perform post-finalization cleanup tasks that relate to the referent but do not involve the referent -- because there is no way for the program to access the referent. The user has to manually call clear() on the reference to clear the referent. I know thats a lot of 'becauses'. But I hope you understand the concept. Leave me a comment if you have any questions or if I have to put it in a different manner.

A sample code is given below:

Date d = new Date();
PhantomReference sr = new PhantomReference(d);
d = null; //remove the strong reference
System.out.println(sr.get()); //null, always


ReferenceQueue:

The reference queue is a data structure in which the GC puts the reference when it clears it. Both Soft and Weak reference has a constructor which takes in a ReferenceQueue. This queue will have the reference when it is collected by the GC. For a PhantomReference, it is mandatory that we pass a ReferenceQueue object in the constructor. That is because the phantom reference's only purpose is to know when its referent is getting cleared. So without a reference queue, the PhantomReference has no purpose as such.

A sample code is given below:

ReferenceQueue refQ = new ReferenceQueue();
//SoftReference sr = new SoftReference(new String("Soft"),refQ);
//WeakReference wr = new WeakReference(new String("Weak"),refQ);
//PhantomReference pr = new PhantomReference(new String("Phantom"), refQ);
Reference r = null;
while((r = refQ.poll())==null)
{
System.out.println ("Polling reference queue");
System.gc();
}
System.out.println (r.get());
//r.clear(); //enable only for phantom reference.


Uncomment one reference object at a time and run through the code to get an understanding. You can use eclipse debug to know what the reference object holds and when the objects are put into the queue when this code is run.

You can refer javaworld for more details.