Pages

Thursday, 26 September 2013

Java References

In this blog post, I want to talk about various java references. In java there are four types of references
  • Strong Reference
  • Soft Reference
  • Weak Reference
  • Phantom Reference
The strongest of these four  is Strong Reference and the weakest is Phantom Reference.  I am not going to talk about the Strong Reference as that is your normal reference but concentrate on the other three reference as that is more interesting.

There are lot of links available that describe the three references but I could not find a post that has code sample explaining the behaviour of them. Hopefully this post will supplement the other posts in your understanding of references. One good link that you should read is this https://weblogs.java.net/blog/2006/05/04/understanding-weak-references. This link explains all the references types in details as such I will not try to explain them but try to supplement them with code samples

Soft Reference

Soft Reference is a reference that is weaker than Strong reference as such JVM will only garbage collect it when it is running out of memory. I will quote a line from the javadoc that would be of our interest http://docs.oracle.com/javase/7/docs/api/java/lang/ref/SoftReference.html
All soft references to softly-reachable objects are guaranteed to have been cleared before the virtual machine throws an OutOfMemoryError
Ok, let us test this. I will have a object named Person as shown below
public class Person {
    private final String name;
    private final int age;

    public Person(final String name, final int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

So, what we will do in our test case is to create a multiple instances of Person object causing it to reclaim the Soft Reference when it is reaching out of memory error

        
        Map <String,SoftReference<Person>> map = new HashMap<String,SoftReference<Person>>();         
        Person person = new Person("tabiul", 35);
        map.put("tabiul",new SoftReference<Person>(person));
        for(int i = 0 ; i < 30000; i++){
            SoftReference<Person> ref = new SoftReference<Person>(new Person("tabiul" + i, 35));
            map.put("tabiul" + i, ref);
            
        }
        System.out.println(map.size());
        System.out.println(map.get("tabiul").get());

Running the above code with the java option -Xms 2m -Xmx 2m we get the correct output
   3001
   Person [name=tabiul, age=35]

But, now if we change the loop from 3000 to let say 4000, the output is as below
   
   4001
   null

As you can see now, as the JVM is reaching its memory limit it garbage collected some of the Soft Reference. So, now the question when is should I use Soft Reference. A good answer is from the javadoc for SoftReference
 Direct instances of this class may be used to implement simple caches; this class or derived subclasses may also be used in larger data structures to implement more sophisticated caches. As long as the referent of a soft reference is strongly reachable, that is, is actually in use, the soft reference will not be cleared. Thus a sophisticated cache can, for example, prevent its most recently used entries from being discarded by keeping strong referents to those entries, leaving the remaining entries to be discarded at the discretion of the garbage collector.

 Weak Reference

A weak reference is weaker then Soft Reference. JVM will reclaim the memory of a weak reference when it is weakly accessible. Below is relevant excerpt from javadoc http://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html
Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues. 
Let us try to simulate this behaviour as shown below

   Person person = new Person("tabiul", 35);
   WeakReference<Person> ref = new WeakReference<Person>(person);
   Map<String,WeakReference<Person>> map = new HashMap<String,WeakReference<Person>>();
   map.put("tabiul", ref);
   person = null;
   System.out.println(map.get("tabiul").get());

You might be surprised to see that it displays this
  Person [name=tabiul, age=35]

But, this make sense as even though the person object is eligible for gc, it is not garbage collected yet. But we can see the correct behaviour if we add the statement below (line 6)
   Person person = new Person("tabiul", 35);
   WeakReference<Person> ref = new WeakReference<Person>(person);
   Map<String,WeakReference<Person>> map = new HashMap<String,WeakReference<Person>>();
   map.put("tabiul", ref);
   person = null;
   System.gc();
   System.out.println(map.get("tabiul").get());

Now we will see what we really want to see
  null

At this point, you might wonder what is the difference between using WeakReference and using normal Strong Reference. Let us find out by changing our code as below
   Person person = new Person("tabiul", 35);
   Map<String,Person> map = new HashMap<String, Person>();
   map.put("tabiul", person);
   person = null;
   System.gc();
   System.out.println(map.get("tabiul"));

Below is the output
  Person [name=tabiul, age=35]

So, you can see that for WeakReference, the gc is more willing to reclaim the memory compare to a normal Strong reference. So, now when should we use a WeakReference. Let me quote from the javadoc itself
Weak references are most often used to implement canonicalizing mappings
Now, you might wonder what the heck is canonicalizing mapping. A good link for this is http://c2.com/cgi/wiki?CanonicalizedMapping

Phantom Reference

Phantom Reference is the weakest of all the references and there is nothing much you can do with it as by the time you know about it, it is ready to be garbage collected. So, you might wonder what is the point of Phantom Reference. Let us turn over to javadoc on this http://docs.oracle.com/javase/7/docs/api/java/lang/ref/PhantomReference.html
Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
 As such, we should use phantom reference if we wish to be know when a certain reference is ready to be garbage collected so that we can do any necessary cleanup.
This situation is very hard to simulate as there is no guarantee on when the gc will decide to garbage collect as such I do not have any code for this. What I can show is here is how we can use it. One possible use case is that we have a large image that is being loaded and we do not want to load a new image until the old one is ready to be garbage collected. Let us see how we can do that

       ImageIcon img = new ImageIcon("icon1.jpg");
       Map<String,PhantomReference<ImageIcon>> map = new HashMap<String,PhantomReference<ImageIcon>>(); 
       ReferenceQueue<ImageIcon> q = new ReferenceQueue<ImageIcon>();
       map.put("icon", new PhantonRefrence<ImageIcon>(img,queue));
       Thread t = new Thread(new LoadNewImage(q,map));  // create a new thread that loads new image when the old is gced
       t.start();
       class LoadNewImage implements Runnable {
           private ReferenceQueue<ImageIcon> q;
           private Map<String,PhantomReference<ImageIcon>> map
           public LoadNewImage(ReferenceQueue<ImageIcon> q, Map<String,PhantomReference<ImageIcon>> map){
              this.q = q;
              this.map = map;
           }

          public void run() {
               //we wait for a object
               try {
                     Reference<ImageIcon> image = (Reference<ImageIcon>) q.remove();
                     if( image != null){
                        map.put("icon", new ImageIcon("icon2.jpg"));  // load new image
                     }
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
          }
      }

That's about it. Any comments and feedback are welcomed.

No comments:

Post a Comment