What Every Java Developer Should Know Strong and Weak References

adminguy's picture
Posted February 24th, 2015 by adminguy

      

 

 

As you may already know objects in Java are created on the heap. But are all objects equally strong? Can some of them be knocked out of the heap without their knowledge? The answer to the latter -- maybe surprisingly -- is yes. Objects can indeed be knocked out of the heap without their knowledge even when they are not candidates for garbage collection. These objects are perfectly good objects which are being referenced and used, but they can still be removed from the heap. Not because they are hacked, but because they are weak references. 

 
In this newsletter we will discuss strong and weak references. We will understand what weak references are, why would we need them, and how to create them. But let's begin with the familiar:

When you  write the following code:

Widget w = new Widget();
Code Sample 1

Widget object is created on the heap, and the variable w holds a reference to that object. As long as the variable w is active, the object which it points to (Widget) will not be garbage collected.

Every Java developer is familiar with this kind of a reference, and even though we rarely use a label to identify it, such a reference is a strong reference. Strong because the object referenced by it cannot be garbage collected against its will. However, Java also has an alternate type of reference - a weak reference. Objects referenced by them may be garbage collected -- if the JVM runs short of memory -- even when they are being used. Creating a weak reference is a way of telling the JVM:

Look dude, I am creating this object as a weak reference. Even though I need it, feel free to garbage collect it if you run out of memory. I know this object can be GC'd any time and am prepared to deal with it.


So how do we create a weak reference? Simple! We use Java's WeakReference class -  like the code below which creates a weak reference to the String "abc":

WeakReference<String> wr = new WeakReference<String>(new String("abc"));
Code Sample 2

The object (in this case a String) which needs to be weakly referenced is wrapped inside a WeakReference object. As many of you might have guessed, we need to deal with an additional level of indirection to use weak references. We can get the String back by using the get method as shown below:

String s = wr.get();
if(s != null ) {
    // great the weak ref has not been garbage collected
} else {
    // oops the weak ref was garbage collected... now I will have to create another one
}
Code Sample 3

The interesting thing about code sample 3 is, we can never assume that the object we are trying to get actually exists. It's a little akin asking for a discount; if you get it, awesome; if not, tough luck, try elsewhere. The most common use for weak references are read-only caches, where losing an object is not disastrous. It just means we have to recreate the object, usually back from the database.

Here's how you would create a simple cache of Widget objects using weak references:
 
public class Widget {
 
    private byte buff [];
    private int id ;
 
    public Widget( int id) {
        //Each Widget object takes approximately 1MB
        this.buff = new byte[1024 * 1000];
        this.id = id;
    }
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        // Ask the user how many Widget objects they want to create
        // Remember each Widget takes a little over 1MB
        Scanner scanner = new Scanner(System.in);
        System. out.println("How many objects do you want to create ?" );
        int count = scanner.nextInt();
 
        Map<Integer, WeakReference<Widget>> weakWidgets = new HashMap<Integer, WeakReference<Widget>>();
 
        System. out.println("Creating " + count + " widgets as weak references.");
 
        for(int i=0; i<count; i++) {
            weakWidgets.put(i, new WeakReference<Widget>( new Widget(i)));
        }
 
        // Here's how we access items from a WeakReference
        for(int i=0; i<count; i++) {
            WeakReference<Widget> weakRef = weakWidgets.get(i);
            Widget ww = weakRef.get();
            // Find out if the WeakWidget is still there or has it been GC'd
            if(ww == null ) {
                System. out.println("Oops WeakWidget " + i + " was garbage collected.");
            } else {
                System. out.println("Awesome WeakWidget " + i + " is still around. Let's use it");
            }
        }
 
    }
 
}
Code Sample 4

If you ever find yourself writing caches such as this, first smack your knuckles for not having looked up the JDK's classes for an existing solution and then take a look at the WeakHashMap class - it does exactly what you need.