[英]When Is The Object Eligible For Garbage Collection?
在下面的代码中,假设已经调用了amethod
。 在什么点/行是myObject
最初引用的对象,有资格获得垃圾收集?
class Test {
private Object classObject;
public void amethod() {
Object myObject = new Object();
classObject = myObject;
myObject = null;
}
}
如果classObject
或amethod
具有public,protected,default或static的访问修饰符,它会影响Object符合垃圾收集的条件吗? 如果是这样,它将如何受到影响?
classObject = myObject;
将被优化出来并且myObject = null;
是它有资格获得垃圾收集的点。 在丢弃对它的所有引用之前,该对象不会成为垃圾收集的候选对象。 Java对象是通过引用分配的,所以当你有
classObject = myObject;
您为堆上的同一对象分配了另一个引用。 所以这一行
myObject = null;
只删除一个引用。 要使myObject
成为垃圾收集的候选者,您必须拥有
classObject = null;
这实际上是通过Java语言规范§12.6.1,实现完成来实现的 :
可以设计优化程序的转换,以减少可达到的对象的数量,使其少于可以被认为可达的对象的数量。 例如,Java编译器或代码生成器可以选择设置将不再用于
null
的变量或参数,以使得此类对象的存储可能更快地被回收。如果对象字段中的值存储在寄存器中,则会出现另一个示例。 然后程序可以访问寄存器而不是对象,并且永远不会再次访问对象。 这意味着该对象是垃圾。 ...
但
...请注意,只有在引用位于堆栈上且未存储在堆中时,才允许进行此类优化。
例如,考虑Finalizer Guardian模式:
class Foo { private final Object finalizerGuardian = new Object() { protected void finalize() throws Throwable { /* finalize outer Foo object */ } } }
终结监护人力
super.finalize
如果子类重写被称为finalize
,不显式调用super.finalize
。如果允许对存储在堆上的引用进行这些优化,那么Java编译器可以检测到
finalizerGuardian
字段永远不会被读取,将其清空,立即收集对象,并尽早调用终结器。 这与意图背道而驰:程序员可能希望在Foo
实例无法访问时调用Foo
终结器。 因此,这种转换不合法:只要外部类对象可以访问,内部类对象就应该可以访问。
此示例可以1:1应用于您的示例,只要该对象由实例字段classObject
,它就不能比包含引用的Test
实例更早地收集垃圾。
但请注意,当使用Test
实例应用于代码时,仍然允许规范中提到的积极优化。 只要两者同时收集Test
实例和引用的对象,就可能发生早于预期的收集。 在这种情况下, §12.6中规定的以下方面适用:
Java编程语言对finalize方法调用没有任何排序。 终结器可以按任何顺序调用,甚至可以同时调用。
因此,完全有可能早于classObject
引用的对象收集Test
实例,而之前调用“inner”对象的终结器。 唯一可以保证的是,当内部对象的终结器运行时,外部对象无法访问(或者具有挂起或并发终结)。 因为在你的例子中,既没有非平凡的终结器,也无论如何......
你的想法是私有对象可能立即被GC,因为没有其他代码能够访问它确实有一些牵引力,但这会混淆Java内存管理的一般语义。 例如,如果该对象实现了finalize
,并且Java语义明确规定何时对象符合垃圾收集条件,那么必须根据规范调用终结器方法。
另请注意,对象反过来可能会引用其他对象,甚至可能会产生更复杂的结果。 更不用说随时可以通过Reflection访问该对象,即使没有代码可以进行该分配,观察到的字段突然变为null
也没有任何意义。
总而言之,有很多原因可以解释为什么你的优化思想不能在更广泛的范围内发挥作用。
来自Book OCA Java SE 7
当对象无法再访问时,该对象被标记为有资格进行垃圾回收,这可能在对象超出范围时发生。 当对象的引用变量被赋值为显式空值或被重新初始化时,也会发生这种情况。
这里没有对象符合垃圾回收的条件,因为您要为同一个对象创建两个引用,并且只给一个引用赋予null,但是其他引用仍指向对象
由于您在classObject
中保存myObject
( 引用被维护 ),因此在释放/卸载Test
实例之前,它(通过classObject引用的内存中的对象)将无法用于垃圾收集。
在下面的代码中,假设已经调用了amethod。 在什么点/行是myObject最初引用的对象,有资格获得垃圾收集?
您的问题是荒谬的,因为您的高级源代码与垃圾收集器看到的低级表示(寄存器,堆栈和全局变量中的全局根)之间存在脱节。
你的短语“有资格进行垃圾收集”可能意味着堆分配的内存块在什么时候无法访问。 因此,您的问题只能通过对堆分配的内容以及生成的代码保留引用的时间做出很多(可疑的)假设来回答。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.