2 回答
TA贡献1876条经验 获得超7个赞
正如Java 是否在对象仍在范围内时可以完成对象中所阐述的那样?,局部变量不会阻止引用对象的垃圾收集。或者,正如这个答案所说,范围只是一个语言概念,与垃圾收集器无关。
我将再次引用规范的相关部分JLS §12.6.1:
可达对象是可以从任何活动线程的任何潜在持续计算中访问的任何对象。
此外,我将答案的示例扩展到
class A {
static volatile boolean finalized;
Object b = new Object() {
@Override protected void finalize() {
System.out.println(this + " was finalized!");
finalized = true;
}
@Override public String toString() {
return "B@"+Integer.toHexString(hashCode());
}
};
@Override protected void finalize() {
System.out.println(this + " was finalized!");
}
@Override public String toString() {
return super.toString() + " with "+b;
}
public static void main(String[] args) {
A a = new A();
System.out.println("Created " + a);
for(int i = 0; !finalized; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
System.out.println("finalized");
}
}
Created A@59a6e353 with B@6aaa5eb0
B@6aaa5eb0 was finalized!
finalized
A@59a6e353 with B@6aaa5eb0 was finalized!
这表明即使是变量在范围内的方法也可以检测到引用对象的终结。此外,从堆变量中引用也不一定会阻止垃圾收集,因为B对象不可访问,因为当包含引用的对象也不可访问时,没有继续计算可以访问它。
值得强调的是,即使使用对象并不总是会阻止其垃圾回收。重要的是,正在进行的操作是否需要对象的内存,而不是每次访问源代码中的对象字段都必须在运行时导致实际的内存访问。该规范指出:
可以设计优化程序的转换,将可到达的对象的数量减少到比那些天真地认为是可到达的要少。[…]
如果对象字段中的值存储在寄存器中,则会出现另一个示例。然后程序可能会访问寄存器而不是对象,并且永远不会再次访问对象。这意味着该对象是垃圾。
这不仅仅是一个理论上的选择。正如在 Java 8 中调用强可达对象的 finalize() 中所讨论的,它甚至可能在对象上调用方法时发生,或者换句话说,this当实例方法仍在执行时,引用可能会被垃圾收集。
防止对象垃圾回收的唯一方法是,如果终结器也对对象进行同步,则对对象进行同步,或者调用Reference.reachabilityFence(object)Java 9 中添加的方法。栅栏方法的后期添加展示了优化器获取的影响在早于想要的垃圾收集问题上,从版本到版本更好。当然,首选的解决方案是编写完全不依赖于垃圾收集时间的代码。
TA贡献1712条经验 获得超3个赞
并不是所有的对象都在堆空间中。但这通常是正确的。Java 已扩展为具有堆栈本地对象,前提是 JVM 可以检测到该对象将仅与堆栈帧一样长。
现在对于堆上的对象,它们在方法中具有本地引用。在处理方法时,与方法运行关联的堆栈帧包含局部变量引用。只要可以使用引用(包括仍在堆栈帧中),对象就不会被垃圾收集。
一旦引用被销毁,并且正在运行的程序无法再访问该对象(因为没有可以访问它的引用),那么垃圾收集器将收集它。
添加回答
举报