In this blog post, I want to talk about various java references. In java there are four types of references
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
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
Running the above code with the java option -Xms 2m -Xmx 2m we get the correct output
But, now if we change the loop from 3000 to let say 4000, the output is as below
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
You might be surprised to see that it displays this
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)
Now we will see what we really want to see
- Strong Reference
- Soft Reference
- Weak Reference
- Phantom Reference
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 anOutOfMemoryError
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
Below is the output
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
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
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 mappingsNow, 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
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.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.
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.