java 可以在对象仍在作用域内时终结该对象吗?

2023-11-30

我一直在研究代码中的一个错误,该错误似乎是由一些“丑陋”的终结器代码引起的。代码大致是这样的

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(使用前将#替换为@)

java 可以在对象仍在作用域内时终结该对象吗? 的相关文章

随机推荐