How to use PhantomReference as finalize () Replacement

Javadoc 8 for PhantomReference says:

Phantom links are most often used to schedule pre-mortem cleanup actions in a more flexible way than is possible with the Java termination mechanism.

So, I tried to create a thread that calls the close() method of the test object, which has the right to garbage collection. run() tries to get all test objects before opening .

In fact, the resulting test objects are all null . The expected behavior is that test objects are retrieved and the close method is called.

No matter how many test objects you create, there is not a single test object that you can catch pre-mortem (you need to increase the timeouts and call the GC several times).

What am I doing wrong? Is this a Java bug?

Managed Test Code:

I tried to create an example of Minimal, Complete and Verifiable, but it is still quite long. I am using java version "1.8.0_121" 32-bit in 64-bit version of Windows 7.

 public class TestPhantomReference { public static void main(String[] args) throws InterruptedException { // Create AutoClose Thread and start it AutoCloseThread thread = new AutoCloseThread(); thread.start(); // Add 10 Test Objects to the AutoClose Thread // Test Objects are directly eligible for GC for (int i = 0; i < 2; i++) { thread.addObject(new Test()); } // Sleep 1 Second, run GC, sleep 1 Second, interrupt AutoCLose Thread Thread.sleep(1000); System.out.println("System.gc()"); System.gc(); Thread.sleep(1000); thread.interrupt(); } public static class Test { public void close() { System.out.println("close()"); } } public static class AutoCloseThread extends Thread { private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>(); private Stack<PhantomReference<Test>> mPhantomStack = new Stack<>(); public void addObject(Test pTest) { // Create PhantomReference for Test Object with Reference Queue, add Reference to Stack mPhantomStack.push(new PhantomReference<Test>(pTest, mReferenceQueue)); } @Override public void run() { try { while (true) { // Get PhantomReference from ReferenceQueue and get the Test Object inside Test testObj = mReferenceQueue.remove().get(); if (null != testObj) { System.out.println("Test Obj call close()"); testObj.close(); } else { System.out.println("Test Obj is null"); } } } catch (InterruptedException e) { System.out.println("Thread Interrupted"); } } } } 

Expected Result:

 System.gc() Test Obj call close() close() Test Obj call close() close() Thread Interrupted 

Actual output:

 System.gc() Test Obj is null Test Obj is null Thread Interrupted 
+4
java garbage-collection finalizer finalize phantom-reference
Apr 09 '17 at 20:54 on
source share
2 answers

get() method on phantom links always return null.

At the moment, the phantom reference is a queued object that has been bound, already compiled by the GC. You need to save the data necessary for cleaning in a separate object (for example, you can subclass PhantomReference ).

Here you can find sample code and a more detailed description about using PhantomReference s.

Unlike the finalizer, the phantom link cannot resurrect an unreachable object. This is its main advantage, although the cost is more complex code support.

+3
Apr 09 '17 at 22:24
source share

This is by design. Unlike finalize() , which makes the object accessible again, objects that reference the Reference object cannot be accessed again. Therefore, when you are going to manage a resource through it, you must store the necessary information in another object. It is not uncommon to use the Reference object for it.

Consider the following modifications to your test program:

 public class TestPhantomReference { public static void main(String[] args) throws InterruptedException { // create two Test Objects without closing them for (int i = 0; i < 2; i++) { new Test(i); } // create two Test Objects with proper resource management try(Test t2=new Test(2); Test t3=new Test(3)) { System.out.println("using Test 2 and 3"); } // Sleep 1 Second, run GC, sleep 1 Second Thread.sleep(1000); System.out.println("System.gc()"); System.gc(); Thread.sleep(1000); } static class TestResource extends PhantomReference<Test> { private int id; private TestResource(int id, Test referent, ReferenceQueue<Test> queue) { super(referent, queue); this.id = id; } private void close() { System.out.println("closed "+id); } } public static class Test implements AutoCloseable { static AutoCloseThread thread = new AutoCloseThread(); static { thread.start(); } private final TestResource resource; Test(int id) { resource = thread.addObject(this, id); } public void close() { resource.close(); thread.remove(resource); } } public static class AutoCloseThread extends Thread { private ReferenceQueue<Test> mReferenceQueue = new ReferenceQueue<>(); private Set<TestResource> mPhantomStack = new HashSet<>(); public AutoCloseThread() { setDaemon(true); } TestResource addObject(Test pTest, int id) { final TestResource rs = new TestResource(id, pTest, mReferenceQueue); mPhantomStack.add(rs); return rs; } void remove(TestResource rs) { mPhantomStack.remove(rs); } @Override public void run() { try { while (true) { TestResource rs = (TestResource)mReferenceQueue.remove(); System.out.println(rs.id+" not properly closed, doing it now"); mPhantomStack.remove(rs); rs.close(); } } catch (InterruptedException e) { System.out.println("Thread Interrupted"); } } } } 

which will print:

 using Test 2 and 3 closed 3 closed 2 System.gc() 0 not properly closed, doing it now closed 0 1 not properly closed, doing it now closed 1 

shows how using the correct idiom ensures timely closure of resources and, unlike finalize() , an object can refuse to clean up after opening, which makes using the correct idiom even more effective, since in this case an additional GC cycle is necessary to restore the object after it completion.

+1
Apr 27 '17 at 13:55 on
source share



All Articles