Search code examples
javareferencegarbage-collectionweak-references

WeakReferenced object is not garbage collected after calling System.gc()


I am a fresh new learner of Java. I'm now learning the concept of WeakReference. I came across a problem which probably looks stupid but I just wanna figure out the reason. The problem is: according to Java doc, "Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed."

So I did this small test:

import java.lang.ref.WeakReference; 

public class A {
    public static void main(String[] args) {
        A a = new A();
        WeakReference<A> wr = new WeakReference<>(a);
        a = null;

        A a1 = wr.get();

        System.out.println(a);
        System.out.println(a1);

        try {
            System.gc();

            Thread.sleep(10000);

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(a1);
    }

    @Override
    protected void finalize( ) {
        System.out.println(Thread.currentThread().getName() + ": See ya, nerds!");
    }
}

However, I noticed that after GC running, wr.get() could still return object which I expected null, and the method finalize() was not invoked. So what went wrong? Thanks for your kind help in advance! :)


Solution

  • The premise of your test is flawed. System.gc() is only a hint to run the garbage collector. It is frequently ignored.

    From the documentation:

    Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

    (Emphasis mine)

    In future, you may use the VM options -verbose:gc and -XX:+PrintGCDetails to see what the garbage collector is doing.


    More importantly, you are also very quickly taking the reference out of the weak reference and putting it back into a strong reference:

    A a = new A();
    WeakReference<A> wr = new WeakReference<>(a);
    a = null; // no strong references remain
    A a1 = wr.get(); // the instance now has a strong reference again
    

    Unless garbage collection occurs between these exact two instructions, the object will not be garbage collected.

    If you remove a1, your code behaved as you would expect when I ran it (though, because of the first part of my answer, your mileage may vary):

    class A
    {
        public static void main(String[] args)
        {
            A a = new A();
            WeakReference<A> wr = new WeakReference<>(a);
            a = null;
    
            System.out.println(a);
    
            try {
                System.gc(); // instance of A is garbage collected
                Thread.sleep(10000);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(wr.get());
        }
    
        @Override
        protected void finalize( )
        {
            System.out.println(Thread.currentThread().getName() + ": See ya, nerds!");
        }
    }