在方法调用期间创建的本地Java对象的生命周期

7
在方法调用中,如果我在该调用期间创建一个对象。那么这些对象何时被垃圾回收?它们是否被放置在堆上,并与堆上的其他对象一起被垃圾回收?还是因为它们不再需要而更早地被垃圾回收了?该方法的执行已经完成。
3个回答

6

方法范围内创建的对象在方法关闭时就可以进行垃圾回收 - 除非该引用作为返回值传递回去。在这种情况下,调用者可能会保留该引用并防止其被垃圾回收。

由于垃圾收集器根据自己的规则运行在自己的线程上,因此您不能确切知道何时清除对象,或者其他地方分配的对象是否也符合条件。


@duffymo:这不取决于函数返回后新创建的对象引用是否存在吗?例如,存储在调用者作用域中的集合中的对象。 - Cratylus
2
对象可以通过许多其他方式逃逸,不仅仅是作为返回值。每次调用对象上的方法或将其用作方法参数时,您都无法轻松确定对象的生命周期。 - mtraut
@mtraut,你能详细说明其他逃逸方式吗?方法参数不同,因为在开始时调用者有对它们的引用。我在考虑方法作用域内创建的对象。我可以想象,在方法内部进行任何创建长期引用的操作(例如将本地引用添加到缓存中等),但我在写回复时想到的是最有可能的情况。 - duffymo
1
我的意思是系统不能轻易地区分以下两种情况:Object m1() { Object a = new Thing(); return null; } 和 Object m2() { Object a = new Thing(); a.toString(); return null; },其中在“toString”中新对象被分配给静态变量。对于“b.anotherCall(a)”也是如此...或者我们正在谈论不同的事情吗? - mtraut
在第二种情况下,新的Thing未分配给静态变量;创建了一个字符串表示,并添加到字符串池中,但据我所知,这并没有保留对“a”的引用。因为没有对象持有对它的引用,所以在方法m2()退出后,对象“a”将被垃圾回收。 - duffymo
显示剩余4条评论

3
这并不容易 - 毕竟每个对象都是在某个方法中创建的。
VM / 编译器需要进行逃逸路径分析,以检测此对象的引用是否会以某种方式逃逸 - 想象一下调用newObject.toString()。你和我知道(希望)这不会有任何危害,因为该对象仍未被引用,因此不会将自身连接到全局变量。但是VM不知道。
虽然现代VM将执行此类分析,并在垃圾收集中特别处理真正短暂的对象,但从“高层次”角度来看,它们只是对象。其他所有内容都是复杂的低级优化。
无论如何,正如duffymo所说 - 什么时候释放这些对象是不确定的。

1

方法的执行已经结束,对象现在超出范围这个事实是无关紧要的。
垃圾回收是运行时系统的隐式操作,它在与您的代码并行的单独线程中实现一个特定的垃圾回收算法。
垃圾回收线程在不可预测的时间运行,根据Java文档,通常每秒钟左右,在内存几乎耗尽的情况下,评估哪些对象有资格进行垃圾回收,即从根指针(例如静态变量)没有对它们的引用。
因此,通过根指针可访问的每个对象都会被标记,然后递归地引用这些对象等等。
这可能意味着扫描整个进程空间。完成后,上一次扫描未“标记”的所有对象将进入空闲列表(被垃圾回收)。
正如您所看到的,这是一项繁重的操作。

该方法的执行已完成。

因此,您已经超出了调用的方法的范围,因为它已经完成,这一事实是无关紧要的。这并不意味着运行时在那个时候就知道对象已经完成(因为GC是并行运行的)。
这不像在C++中,在方法结束时程序员会调用delete来删除对象,因为它不再需要。在Java中,没有“delete”会在方法结束时自动调用。
GC线程最终会意识到由方法分配在堆上的对象没有更多的引用,并对其进行垃圾回收。
如果您想要,在任何时候都可以通过以下方式调用GC:

System.gc();

但是垃圾回收器迟早会运行。
需要注意的一点是,只要根指针引用了方法,它就不能被垃圾回收器回收。
因此,如果在您的方法中通过new在堆上创建一个对象,并将引用存储在静态容器中或返回给调用者,则该对象的生命周期超出了该方法。


垃圾回收自Smalltalk时代以来已经有了很大的改进。现在没有任何商业级别的虚拟机再像这样做了。关于实际算法,可以在谷歌上找到很多信息。 - mtraut
System.gc() 更多或少是向虚拟机建议现在触发gc的好主意。您不能依赖此被确认(即使VM同意),也不能依赖gc()后垃圾被收集。这始终是异步的。 - mtraut
@mtraut: 我也倾向于这样做。但是Javadocs似乎表明了相反的情况:当从方法调用(System.gc)返回控制时,Java虚拟机已经尽最大努力从所有废弃对象中回收空间 - Cratylus
我的评论中的句子应该是“(即使虚拟机同意)你也不能依赖...”。正如你引用的那样,这是最好的努力。这意味着由虚拟机决定 - 谁最懂得。 - mtraut
对于你的第一句话要挑剔一下:对象并没有超出作用域——引用它的变量已经超出了作用域。这基本上是围绕这个领域所有混淆的根源。 - DJClayworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接