Search code examples
javareferenceweak-references

Weakly referenced object won't get garbage collected


My concern is about an instance of an object that was once strongly referenced, but after an explicit null assignment to its strong reference and after an explicit System.gc() call, the instance is still reachable via the weak reference. If I understand correctly, when a referred object has only weak references left, the referent is guaranteed to be cleared in the next GC session. What am I missing?

Reference code:

public class References {
    public static void main(String[] args) {

        Example strongReferenceWrappedInWeak = new Example(42);
        strongReferenceWrappedInWeak.printA();

        WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

        System.gc();

        Example retrievedExample = exampleWeakReference.get();
        retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared

        strongReferenceWrappedInWeak = null; //eligible for garbage collection

        System.gc();

        Example retrievedExampleTwo = exampleWeakReference.get(); //should be null
        retrievedExampleTwo.printA(); //should throw NPE
    }
}

class Example {
    private int a;

    Example(int a) {
        this.a = a;
    }

    void printA() {
        System.out.println(this.a);
    }
}


Solution

  • strongReferenceWrappedInWeak = null does not make the Example object instance eligible for garbage collection, because retrievedExample still maintains a strong reference to it.

    To fix, add retrievedExample = null;

    Example strongReferenceWrappedInWeak = new Example(42);
    strongReferenceWrappedInWeak.printA();
    
    WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);
    
    System.gc();
    
    Example retrievedExample = exampleWeakReference.get();
    retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared
    
    retrievedExample = null;
    strongReferenceWrappedInWeak = null; //now eligible for garbage collection
    
    System.gc();
    
    Example retrievedExampleTwo = exampleWeakReference.get(); //will be null
    retrievedExampleTwo.printA(); //will throw NPE
    

    Alternatively, don't create a strong reference with a local variable, just call the method directly off the weak reference. That way you don't accidentally leave a strong reference behind, as you did. *(During the printA() call, the this reference is a strong reference, so object cannot be GC'd during the call)*

    Example strongReferenceWrappedInWeak = new Example(42);
    strongReferenceWrappedInWeak.printA();
    
    WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);
    
    System.gc(); //does not collect object, since strong reference still exists
    
    exampleWeakReference.get().printA(); //works
    
    strongReferenceWrappedInWeak = null; //eligible for garbage collection
    System.gc(); //collects object, since it is now weakly referenced only
    
    exampleWeakReference.get().printA(); //throws NPE
    

    Output (from both)

    42
    42
    Exception in thread "main" java.lang.NullPointerException
        at Test.main(Test.java:**)
    

    Tested on Java 13