gpt4 book ai didi

java - 如何使用 PhantomReference 作为 finalize() 的替代

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:08:22 27 4
gpt4 key购买 nike

Javadoc 8 PhantomReference 状态:

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.

所以我尝试创建一个线程来调用符合垃圾回收条件的测试对象的 close() 方法。 run() 尝试获取所有测试对象pre-mortem

实际上检索到的测试对象都是null。预期的行为是检索测试对象并调用 close 方法。

无论您创建多少个测试对象,都没有一个测试对象可以被捕获预先分析(您必须增加超时并多次调用 GC)。

我做错了什么?这是 Java 错误吗?

可运行的测试代码:

我尝试创建一个最小、完整且可验证的示例,但它仍然很长。我在 Windows 7 64 位上使用 java 版本“1.8.0_121” 32 位。

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

最佳答案

这是设计使然。与使对象再次可达的 finalize() 不同,只能由 Reference 对象引用的对象不能再次可达。所以当你要通过它管理资源时,你必须将必要的信息存储到另一个对象中。为它使用 Reference 对象本身并不罕见。

考虑对您的测试程序进行以下修改:

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");
}
}
}
}

将打印:

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

显示如何使用正确的习惯用法确保资源及时关闭,并且与 finalize() 不同,对象可以选择退出事后清理,这使得使用正确的习惯用法更加高效,因为在这种情况下,不需要额外的 GC 周期来在完成后回收对象。

关于java - 如何使用 PhantomReference 作为 finalize() 的替代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43311825/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com