我一直在研究代码中的一个错误,该错误似乎是由一些“丑陋”的终结器代码引起的。代码大致是这样的
public class A {
public B b = new B();
@Override public void finalize() {
b.close();
}
}
public class B {
public void close() { /* do clean up our resources. */ }
public void doSomething() { /* do something that requires us not to be closed */ }
}
void main() {
A a = new A();
B b = a.b;
for(/*lots of time*/) {
b.doSomething();
}
}
我认为正在发生的是a
被检测为在第二行之后没有引用main()
并由终结器线程进行 GC 和终结 - 而for
循环仍在发生,使用b
while a
仍然“在范围内”。
这合理吗? java 是否允许在对象超出范围之前对其进行 GC?
Note: I know在终结器内做任何事情都是不好的。这是我继承并打算修复的代码 - 问题是我是否正确理解了根本问题。如果这是不可能的,那么我的错误的根源一定是更微妙的东西。
Java 能否在对象仍在作用域内时终结该对象?
Yes.
然而,我在这里很迂腐。Scope是决定名称有效性的语言概念。一个对象是否可以被垃圾回收(并因此最终确定)取决于它是否是可达的.
The 来自 ajb 的回答通过引用 JLS 中的重要段落,几乎获得了它(+1)。但我不认为它直接适用于这种情况。吉林森§12.6.1还说:
A 可达的object 是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。
现在考虑将其应用于以下代码:
class A {
@Override protected void finalize() {
System.out.println(this + " was finalized!");
}
public static void main(String[] args) {
A a = new A();
System.out.println("Created " + a);
for (int i = 0; i < 1_000_000_000; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
// System.out.println(a + " was still alive.");
}
}
在 JDK 8 GA 上,这将最终确定a
每一次。如果您取消注释println
在最后,a
永远不会最终确定。
随着println
注释掉后,人们可以看到可达性规则是如何应用的。当代码到达循环时,线程不可能有任何访问权限a
。因此它是无法访问的,因此会受到最终确定和垃圾回收的影响。
注意名字a
还是in scope因为人们可以使用a
封闭块内的任何位置——在本例中main
方法体——从其声明到块的末尾。 JLS 中涵盖了确切的范围规则§6.3。但实际上,正如您所看到的,范围与可达性或垃圾收集无关。
为了防止对象被垃圾收集,您可以将对其的引用存储在静态字段中,或者如果您不想这样做,则可以通过稍后在相同的方法中使用它来保持它的可访问性 -消耗循环。调用一个无害的方法就足够了,比如toString
on it.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)