В документации Javadoc 8 для PhantomReference говорится:
Фантомные ссылки чаще всего используются для планирования действий предварительной очистки более гибким способом, чем это возможно с помощью механизма завершения Java.
Итак, я попытался создать поток, который вызывает метод close()
тестового объекта, который подходит для сборки мусора. run()
пытается получить все предварительные тестовые объекты.
Фактически все полученные тестовые объекты - это null
. Ожидаемое поведение состоит в том, что тестовые объекты извлекаются и вызывается close
method.
Независимо от того, сколько тестовых объектов вы создаете, нет ни одного тестового объекта, который можно было бы поймать pre-mortem (вам нужно увеличить таймауты и вызвать GC несколько раз).
Что я делаю неправильно? Это ошибка Java?
Запускаемый тестовый код:
Я попытался создать минимальный, полный и проверяемый пример, но он все еще довольно длинный. Я использую java version "1.8.0_121"
32-битную на 64-битной 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");
}
}
}
}
Ожидаемый результат:
System.gc()
Test Obj call close()
close()
Test Obj call close()
close()
Thread Interrupted
Фактический выход:
System.gc()
Test Obj is null
Test Obj is null
Thread Interrupted
Test testObj = mReferenceQueue.remove().get();
всегда будет нулевым. Измените этот блок кода наmReferenceQueue.remove().close()
, и он будет работать.queue.remove()
будет правильно блокировать и всегда возвращать вам объект. Нет необходимости проверятьnull
. - person Pacerier   schedule 19.09.2017mReferenceQueue.remove()
вернет объектReference<? extends Test>
, а не объектTest
, поэтому я не могу вызватьmReferenceQueue.remove().close()
. Может быть, вы можете предоставить более подробную информацию. - person notes-jj   schedule 19.09.2017