java GC如何在范围中间工作
我有这样的代码:
public void foo()
{
Object x = new LongObject();
doSomething(x);
//More Code
// x is never used again
// x = null helps GB??
Object x2 = new LongObject();
doSomething(x2);
}
我希望x分配的内存可以由GC释放,如果需要的话。但我不知道是否有必要设置为null,或者编译器是否需要这样做
# 1 楼答案
事实上,JIT对引用(字节码级别的引用存储为当前帧中的插槽)进行活动性分析。如果一个引用不再被读取,它的插槽可以被重用,JIT就会知道这一点。当引用对象的变量仍在词法范围内时,完全有可能对对象进行垃圾收集,只要编译器和JIT能够证明该变量永远不会被取消引用
重点是:scope是语言的一种构造,它指定了像
x
这样的名称在程序代码文本中的任何一点上的含义生命周期是对象的属性,而JIT和GC通常以不明显的方式来管理它请记住,JIT可以在运行时重新编译代码,并在看到代码执行时对代码进行优化。除非你真的确定自己知道自己在做什么,否则不要试图智胜JIT。编写正确的代码,让JIT完成它的工作,只有当你有证据表明JIT做得不够好时才担心它
# 2 楼答案
从字面上回答您的问题,编译器(从源代码到字节码编译器)从不插入
null
赋值,但仍然没有必要将变量赋值给null
-通常正如this answer所解释的,作用域是编译时的事情,从形式上讲,如果对象不能“be accessed in any potential continuing computation from any live thread”,那么它就有资格进行垃圾收集。但无法保证特定实现将识别哪个合格对象。正如链接的答案所解释的,JIT编译的代码只会保留对随后将被访问的对象的引用。这可能比您预期的更进一步,允许垃圾收集看起来在源代码中使用的对象,因为运行时优化可能会转换代码并减少实际的内存访问
但在解释模式下,分析不会进行到这一步,而且当前堆栈帧中可能存在对象引用,阻止引用对象的收集,尽管之后没有使用该变量,甚至源代码中没有使用该变量。无法保证在执行方法时从解释代码切换到编译代码能够摆脱这种悬而未决的引用。当实际的大量计算发生在
doSomething
内时,热点优化器甚至不太可能考虑编译foo()
不过,这很少是一个问题。仅在初始化或第一次执行期间才会运行解释,即使这些对象很大,如果收集此类对象的时间稍晚,也很少会出现问题。一个普通的应用程序由数百万个对象组成
然而,如果您曾经认为可能存在问题,您可以轻松地解决这个问题,而无需将
null
分配给变量。限制范围:除了分配
null
之外,将变量的范围限制在实际使用范围内可以提高源代码的质量,即使在对编译后的代码没有影响的情况下也是如此。虽然作用域纯粹是源代码,但它可能会对字节码产生影响。在上面的代码中,编译器将重用堆栈帧中x
的位置来存储x2
,因此在第二次doSomething
执行期间不存在对第一个LongObject
的悬空引用如上所述,内存管理很少需要这一点,提高源代码质量应该是驱动您做出决定的因素,而不是帮助垃圾收集器的尝试