Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why the reference don't put into reference queue when finalize method overrided

public class Test {
    public static void main(String[] args) throws Exception {
        A aObject = new A();

        ReferenceQueue<A> queue = new ReferenceQueue<>();
        PhantomReference<A> weak = new PhantomReference<>(aObject, queue);

        aObject = null;
        System.gc();

        TimeUnit.SECONDS.sleep(1);

        System.out.println(queue.poll());
    }
}

class A{
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();

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

The result is:

finalize
null

but if i delete the finalize method in class A, the result is:

java.lang.ref.PhantomReference@5b2c9e5d

so, the result shows that when I override the finalize method, the weak object isn't put into the reference queue, is that because the aObject resurrected? But I don't do anything in the finalize method

like image 520
gesanri Avatar asked Jan 09 '18 11:01

gesanri


2 Answers

With a nontrivial finalize, Java knows the object is unreachable* before finalize runs, but it doesn't know the object is still unreachable after finalize.

It has to wait for the object to be deemed unreachable again in another GC cycle before it can enqueue the phantom reference.

*not quite unreachable in the terminology of the java.lang.ref docs, but neither strongly, softly, nor weakly reachable

like image 117
user2357112 supports Monica Avatar answered Oct 13 '22 01:10

user2357112 supports Monica


Very interesting observation. Here is what is happening:

When class has non-trivial (non empty in OP case) finalize method the JVM will create a java.lang.ref.Finalizer (which is a subclass of Reference) object and point it to our referent, in this case A object. This in turn will prevent PhantomReference to enqueue it since A is already referenced by Finalizer.

This was observer by me in debugger using Java 1.8 and also described in detail here.

The observation from @Turing85 is expected since when we remove all statements inside finalize method it becomes trivial and behaves just like any class without finalize method and wont be referenced by Finalizer.

Update:

The question was asked if Finalizer will clear its reference to A at all. The JVM does clear it on the subsequent GC runs, which in turn finally allows PhantomReference to enqueue A into its reference queue.

For example running the below code with non-trivial finalize method will get non null PhantomReference from its reference queue.

public static void main(String[] args) throws Exception {
    A aObject = new A();

    ReferenceQueue<A> queue = new ReferenceQueue<>();
    PhantomReference<A> pr = new PhantomReference<>(aObject, queue);

    aObject = null;
    System.gc();

    TimeUnit.SECONDS.sleep(1);

    System.gc();

    TimeUnit.SECONDS.sleep(1);

    System.out.println( queue.poll() );
}

Prints:

finalize 
java.lang.ref.PhantomReference@15db9742
like image 41
tsolakp Avatar answered Oct 12 '22 23:10

tsolakp