Strong, Soft, Weak and Phantom References (Java)

doug
Sat, 15 Jan 2011 00:48:48 +0000

There are four distinct forms of references in the JVM, and indeed many of these apply to other garbage collected languages.

Strong references

Soft references

Weak references

Phantom references

It's important to know the differences, what affect they have on the collector and when you should be using them.

Strong references

Strong references never get collected

package org.neverfear.leaks;

/*
 * URL: http://neverfear.org/blog/view/150/Java_References
 * Author: doug@neverfear.org
 */
public class ClassStrong {

    public static class Referred {
        protected void finalize() {
            System.out.println("Good bye cruel world");
        }
    }

    public static void collect() throws InterruptedException {
        System.out.println("Suggesting collection");
        System.gc();
        System.out.println("Sleeping");
        Thread.sleep(5000);
    }

    public static void main(String args[]) throws InterruptedException {
        System.out.println("Creating strong references");

        // This is now a strong reference.
        // The object will only be collected if all references to it disappear.
        Referred strong = new Referred();

        // Attempt to claim a suggested reference.
        ClassStrong.collect();

        System.out.println("Removing reference");
        // The object may now be collected.
        strong = null;
        ClassStrong.collect();

        System.out.println("Done");
    }

}

Soft references

Soft references only get collected if the JVM absolutely needs the memory. This makes them excellent for implementing object cache's.

package org.neverfear.leaks;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

/*
 * A sample for Detecting and locating memory leaks in Java
 * URL: http://neverfear.org/blog/view/150/Java_References
 * Author: doug@neverfear.org
 */
public class ClassSoft {

    public static class Referred {
        protected void finalize() {
            System.out.println("Good bye cruel world");
        }
    }

    public static void collect() throws InterruptedException {
        System.out.println("Suggesting collection");
        System.gc();
        System.out.println("Sleeping");
        Thread.sleep(5000);
    }

    public static void main(String args[]) throws InterruptedException {
        System.out.println("Creating soft references");

        // This is now a soft reference.
        // The object will be collected only if no strong references exist and the JVM really needs the memory.
        Referred strong = new Referred();
        SoftReference<Referred> soft = new SoftReference<Referred>(strong);

        // Attempt to claim a suggested reference.
        ClassSoft.collect();

        System.out.println("Removing reference");
        // The object may but highly likely wont be collected.
        strong = null;
        ClassSoft.collect();

        System.out.println("Consuming heap");
        try
        {
            // Create lots of objects on the heap
            List<ClassSoft> heap = new ArrayList<ClassSoft>(100000);
            while(true) {
                heap.add(new ClassSoft());
            }
        }
        catch (OutOfMemoryError e) {
            // The soft object should have been collected before this
            System.out.println("Out of memory error raised");
        }

        System.out.println("Done");
    }

}

Weak references

Weak references only get collected if no other object references it except the weak references. This makes them perfect for keeping meta data about a particular object for the life time of the object.

package org.neverfear.leaks;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/*
 * A sample for Detecting and locating memory leaks in Java
 * URL: http://neverfear.org/blog/view/150/Java_References
 * Author: doug@neverfear.org
 */
public class ClassWeak {

    public static class Referred {
        protected void finalize() {
            System.out.println("Good bye cruel world");
        }
    }

    public static void collect() throws InterruptedException {
        System.out.println("Suggesting collection");
        System.gc();
        System.out.println("Sleeping");
        Thread.sleep(5000);
    }

    public static void main(String args[]) throws InterruptedException {
        System.out.println("Creating weak references");

        // This is now a weak reference.
        // The object will be collected only if no strong references.
        Referred strong = new Referred();
        WeakReference<Referred> weak = new WeakReference<Referred>(strong);

        // Attempt to claim a suggested reference.
        ClassWeak.collect();

        System.out.println("Removing reference");
        // The object may be collected.
        strong = null;
        ClassWeak.collect();

        System.out.println("Done");
    }

}

Phantom references

Phantom references are objects that can be collected whenever the collector likes. The object reference is appended to a ReferenceQueue and you can use this to clean up after a collection. This is an alternative to the finalize() method and is slightly safer because the finalize() method may ressurect the object by creating new strong references. The PhantomReference however cleans up the object and enqueues the reference object to a ReferenceQueue that a class can use for clean up.

package org.neverfear.leaks;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
import java.util.Map;

/*
 * A sample for Detecting and locating memory leaks in Java
 * URL: http://neverfear.org/blog/view/150/Java_References
 * Author: doug@neverfear.org
 */
public class ClassPhantom {

    public static class Referred {
        // Note that if there is a finalize() method PhantomReference's don't get appended to a ReferenceQueue
    }

    public static void collect() throws InterruptedException {
        System.out.println("Suggesting collection");
        System.gc();
        System.out.println("Sleeping");
        Thread.sleep(5000);
    }

    public static void main(String args[]) throws InterruptedException {
        System.out.println("Creating phantom references");

        // The reference itself will be appended to the dead queue for clean up.
        ReferenceQueue dead = new ReferenceQueue();

        // This map is just a sample we might use to locate resources we need to clean up.
        Map<Reference,String> cleanUpMap = new HashMap<Reference,String>();

        // This is now a phantom reference.
        // The object will be collected only if no strong references.
        Referred strong = new Referred();

        PhantomReference<Referred> phantom = new PhantomReference(strong, dead);
        cleanUpMap.put(phantom, "You need to clean up some resources, such as me!");

        strong = null;

        // The object may now be collected
        ClassPhantom.collect();

        // Check for
        Reference reference = dead.poll();
        if (reference != null) {
            System.out.println(cleanUpMap.remove(reference));
        }
        System.out.println("Done");
    }

}

ReferenceQueue

You saw me use the reference queue class in the previous example. A ReferenceQueue instance can be supplied as an argument to SoftReference, WeakReference or PhantomReference. When an object is collected the reference instance itself will be enqueued to the supplied ReferenceQueue. This allows you to perform clean up operations on the object. This is useful if you are implementing any container classes that you want to contain a Soft, Weak or Phantom reference and some associated data because you can get notified via the ReferenceQueue which Reference was just collected.

WeakHashMap class

There is also a convience WeakHashMap that wraps all keys by a weak reference. Allowing you to easily store meta data against an object and have the map entry including the meta data removed and collected when the original object itself is unreachable.

package org.neverfear.leaks;

import java.util.Map;
import java.util.WeakHashMap;

/*
 * A sample for Detecting and locating memory leaks in Java
 * URL: http://neverfear.org/blog/view/150/Java_References
 * Author: doug@neverfear.org
 */
public class ClassWeakHashMap {

    public static class Referred {
        protected void finalize() {
            System.out.println("Good bye cruel world");
        }
    }

    public static void collect() throws InterruptedException {
        System.out.println("Suggesting collection");
        System.gc();
        System.out.println("Sleeping");
        Thread.sleep(5000);
    }

    public static void main(String args[]) throws InterruptedException {
        System.out.println("Creating weak references");

        // This is now a weak reference.
        // The object will be collected only if no strong references.
        Referred strong = new Referred();
        Map<Referred,String> metadata = new WeakHashMap<Referred,String>();
        metadata.put(strong, "WeakHashMap's make my world go around");

        // Attempt to claim a suggested reference.
        ClassWeakHashMap.collect();
        System.out.println("Still has metadata entry? " + (metadata.size() == 1));
        System.out.println("Removing reference");
        // The object may be collected.
        strong = null;
        ClassWeakHashMap.collect();

        System.out.println("Still has metadata entry? " + (metadata.size() == 1));

        System.out.println("Done");
    }

}