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 closemethod 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