Javadoc 8 for PhantomReference states:
Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
So I tried creating a thread that is calling the close()
method of a Test Object that is eligible for garbage collection. The run()
tries to get all Test Objects pre-mortem.
Actually the retrieved Test Objects are all null
. The expected behavior is, that the 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 could be caught pre-mortem (You have to increase the timeouts and call GC multiple times).
What am I doing wrong? Is this a Java Bug?
Runnable Test Code:
I tried to create a Minimal, Complete, and Verifiable example, but it's still quite long. I use java version "1.8.0_121"
32-bit on Windows 7 64-bit.
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 Output:
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
Test testObj = mReferenceQueue.remove().get();
will always be null . Change that block of code tomReferenceQueue.remove().close()
and it'll work.queue.remove()
will block correctly and always return you an object. There's no need to test fornull
. – GodavarimReferenceQueue.remove()
will return aReference<? extends Test>
Object and not theTest
Object, so I can't callmReferenceQueue.remove().close()
. Maybe you can provide more details. – Gog