Search code examples
javagetreferencenullweak

Java WeakReference is still holding valid reference when referent is no longer valid


I'm confused with this program:

class Point {
    private final int x;
    private final int y;
}
public class App
{
    WeakReference<Point> newPoint() {
        Point referent = new Point();
        return new WeakReference<Point>(referent); // after return, stack parameter referent is invalid.
    }
    public static void main( String[] args ) {
        App a = new App();
        WeakReference<Point> wp = a.newPoint(); // wp is hold valid or invalid reference?
        System.out.println(wp.get()); // not null
    }
}

I knew that if weak reference is pointing to an object that's no longer alive, its get() should return null. But in my code, seems its still alive.

Where did I get wrong?


Solution

  • I knew that if weak reference is pointing to an object that's no longer alive, its get() should return null. But in my code, seems its still alive.

    Your understanding is imprecise, especially where it relies on the idea of aliveness. Reference objects in general and WeakReference objects in particular are not directly concerned with any of the senses of aliveness that I recognize. Rather, they are concerned with reachability.

    The API docs for java.lang.Reference#get() (which is not overridden by WeakReference) say this:

    Returns this reference object's referent. If this reference object has been cleared, either by the program or by the garbage collector, then this method returns null.

    Note well that the condition for get() returning null is rather specific: the reference object has been cleared. This is achieved for a given instance by invoking that instance's clear() method. As the doc indicates, this may be done by the garbage collector or by the application.

    Among the key differences between Reference subclasses is the conditions under which the garbage collector will perform such clearing. For WeakReferences, the API docs say:

    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 [...].

    Thus, until the garbage collector determines that a given object is (only) weakly reachable, it will not clear weak references to that object. The garbage collector probably does not run at all during the brief run of your small program, and if it did run, it would be surprising for it to be timed correctly to observe the Point in question to be weakly reachable before the reference's get() method is invoked.

    You could try forcing a GC run by invoking System.gc() at the appropriate place. I anticipate that doing so will result in the weak reference being cleared. That would be for demonstrative purposes only, however. Generally speaking, you should rely on Java to perform GC when appropriate, not force it.