PhantomReference的Javadoc 8 规定:
幻像引用最常用于以比Java终结机制更灵活的方式安排 pre-mortem 清理操作。
所以我尝试创建一个调用符合垃圾收集条件的测试对象的 df = df.loc[:, df.isnull().sum() < 0.8*df.shape[0]]
方法的线程。 close()
尝试让所有测试对象预先验证。
实际上,检索到的测试对象都是run()
。预期的行为是,检索测试对象并调用null
方法。
无论您创建多少个测试对象,都没有一个测试对象可以被捕获预先验证(您必须增加超时并多次调用GC)。
我做错了什么?这是一个Java Bug吗?
我尝试创建最小,完整且可验证的示例,但它仍然很长。我在Windows 7 64位上使用close
32位。
java version "1.8.0_121"
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
答案 0 :(得分:3)
get()
方法总是返回null。
目前,幻像引用是入队对象,它已经被GC收集了引用。您需要在单独的对象中存储清理所需的数据(例如,您可以继承PhantomReference
)。
Here您可以找到有关使用PhantomReference
的示例代码和更详细的说明。
与终结器不同,幻像引用无法恢复无法到达的对象。这是它的主要优势,虽然成本更复杂,支持代码。
答案 1 :(得分:2)
这是设计的。与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()
显示如何使用正确的习惯用法确保及时关闭资源,与public class DisplayActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//Display list of hospitals
String[] doctors = getResources().getStringArray(R.array.hospitalList); // Get array List ID
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, doctors);
ListView lv = (ListView) findViewById(R.id.hospitalListView);
lv.setAdapter(adapter);
}
}
不同,对象可以选择退出事后清理,这样可以更有效地使用正确的习惯用法,因为在这种情况下,没有最终确定后需要额外的GC循环来回收对象。