Weak Reference. What?

Q: What are weak references good for?
A: They have something to do with garbage collection.

While the answer above is certainly better than not knowing about weak references at all; any aspiring senior Java developer should know a bit more about them. Especially, when we consider that they are around since Java version 1.2. So, let’s dive a little deeper into this topic.

Strong references are ordinary Java references we use every day. For example:

Widget widget = new Widget();

Objects survive garbage collection and stay in the memory heap as long as they are reachable through strong references.

Let’s just stop here for a minute. Those who are only interested in weak references can skip this section or come back to it later. The rest of you should try to answer the question: Where does the garbage collector start working from? There are special objects called Garbage-Collection roots that are always reachable and serve as starting points for GC operations. There are the following four different kinds of such GC roots in Java:

  • Local variables kept alive by the stack of a thread.
  • Active Java threads are always considered live objects.
  • Static references are kept alive as long as the Class itself is not garbage collected.
  • JNI references are Java objects that the native code has created as a part of a Java Native Interface call. Such objects represent a very special form of GC root and are treated distinctively because the JVM has no idea if they are still referenced by the native code or not.

Weak references are not strong enough to keep the objects from being garbage-collected. You can create a weak reference like this:

WeakReference weakWidget = new WeakReference(widget);

In order to get back the actual widget, you call weakWidget.get(). Because the widget can get garbage-collected at any time, such a call can suddenly start returning null. There is a related class WeakHashMap where the keys (not the values!) are weak references and when the key gets garbage-collected the value is removed from the map automatically. This behavior makes it an ideal candidate for implementing cache functionality.

Would you like to know when a weakly referenced object has been garbage collected? This is what ReferenceQueue is for. If you pass it into the constructor of a weak reference it will be automatically put on the queue once the referenced object becomes a garbage.

There are different degrees of weakness. Soft references are between strong and weak references. They are less eager to throw away their object. In practice, they retain it as long as there is plenty of memory – but this is not a guarantee.

Phantom references are the weakest form of a reference: their get() method always returns null. Phantom references are only enqueued when the object has been already finalized. Which leads us to the only scenario I can think of in which phantom references can be useful: to determine when exactly a given object has been garbage-collected. You can create a phantom reference calling it’s only constructor:

PhantomReference(T referent, ReferenceQueue< ? Super T> q)

Did you notice something? Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable [5].

I Hope you found this post useful 😉

Resources:
[1] https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references
[2] https://www.dynatrace.com/resources/ebooks/javabook/how-garbage-collection-works
[3] https://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html
[4] https://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
[5] https://docs.oracle.com/javase/7/docs/api/java/lang/ref/PhantomReference.html