当调用System.gc()或finalize()方法时,JVM内部会发生什么?

5

当调用System.gc()或finalize()方法时,Java虚拟机(JVM)内部会发生什么?

这是否真正回收了垃圾或降低了性能?


1
请记住,垃圾回收行为取决于实现方式,因此在不同的虚拟机中会有所不同。 - Decio Lira
6个回答

8
当您调用System.gc()时,实际发生的情况取决于JVM。JVM将此调用视为建议,表明现在可能是运行垃圾回收器的好时机,因此不要依赖它。
当JVM确定没有更多可达到该对象的引用时,垃圾收集器会在对象上运行finalize()方法。您不应在代码中调用它。请记住,finalize()不是在程序失去最后一个引用时调用的,而是在将来运行垃圾收集器时调用的,因此不要依赖它在特定时间(或根本)发生。

3

1
通过调用System.gc(),垃圾回收器会运行,正如其名称所示。在这一点上,当对象真正被移除时,在它们消失之前,将调用这些对象的finalize()
最后一个问题不应该有"or"。它是垃圾收集和性能降低。
通常情况下,您不应该关心gc,因为它可以为您提供非常好的服务。但是,在某些使用场景中,您可能希望在特定时间点摆脱大量对象;那么这是可行的。

13
System.gc()并不保证会触发垃圾回收。它只是向JVM建议现在是运行垃圾回收的合适时机,但最终是否执行取决于JVM自身的决策。 - coobird
如果调用System.gc(),我预计gc不运行的时间会很少。另一方面,只有在达到阈值时才会完全运行gc。通过调用gc(),通常只是在之前进行垃圾回收。 - Norbert Hartl
1
如果您只有一个CPU,而且需要进行System.gc()清理的工作量很大,那么在清理完成之前,您可能会看到真正的性能下降。如果您的JVM任务接近内存极限并且自动执行严格的GC操作,则这可能会非常痛苦。但是,如果您有多个CPU,则性能问题要少得多,因为GC可以在其他CPU上发生,而您的JVM的其余部分则可以愉快地运行... - jwl
你有相关链接、信息或证明吗?我已经很久没有关注这些事情了。但是,如果JVM不使用对象空间分区或类似的东西,你的程序将始终停留在full gc上。存在许多技术来尽可能地推迟进行full gc的时刻,例如分代gc。它们也使full gc变得不那么痛苦。 - Norbert Hartl

0

System.gc() 会清理内存,并使用 finalize() 来处理单个对象。


0

finalize()是一种在对象准备进行垃圾回收(当对象没有强引用时)之前执行最后一段代码的方法。

那么它应该在什么情况下使用呢? 只有在以下两种情况下:

  1. 作为安全措施,确保关闭某些服务或进行某些所需的最终更改。例如,InputStream类使用它来关闭I/O流。例如,您已经创建了一个BufferedInputStream实例。在使用完毕后,手动应该将其close()。但是因为人们可能会忘记这样做,finalize()充当了一个安全网来close()流。
  2. 在使用Native时。由于垃圾收集器无法控制本地对象,因此finalize()可以用作回收它的一种方式。

除了上述两种情况外,永远不要使用它。要理解原因,我们需要了解对象的功能和生命周期。

介绍:有一个单独的守护线程称为finalizer线程,负责调用finalize()方法。Finalization队列是放置准备调用finalize()方法的对象的队列。

当创建一个对象时,JVM会检查该对象是否有finalize()方法。如果有,则它在内部记录该特定对象具有finalize()方法。
当对象准备进行垃圾回收时,垃圾收集器线程会从(1)中提到的表中检查该特定对象是否具有finalize()方法。
2a) 如果没有,则将其发送到垃圾回收。 2b) 如果有,则将其添加到终结队列中。并从表(1)中删除该对象的条目。
Finalizer线程不断轮询队列。对于队列中的每个对象,都会调用其finalize()方法。调用finalize()方法后,再次执行(2)中的循环。如果此对象仍然没有强引用,则将其发送到GC。如果有,则始终调用(2a),因为条目已在(2b)中删除。
Basically finalize() method is only called once.

那么上述循环有什么问题呢?

从(1)来看,它需要额外的时间来创建对象。在Java中,内存分配比malloc/calloc等快5倍到10倍。所有节省下来的时间都会在将对象记录在表格中的过程中丢失。我曾经尝试过。在循环中创建100000个对象,并测量程序在两种情况下终止所需的时间:一种是没有finalize(),另一种是有finalize()。发现后者要快20%。

从(2b)来看:内存泄漏和饥饿。如果队列中的对象引用了大量的内存资源,那么除非该对象准备进行GC,否则所有这些对象都不会被释放。如果所有对象都是重量级对象,则可能会出现短缺。

从(2b)来看:因为finalize()只被调用一次,如果在finalize()中有对“this”对象的强引用,那么下一次对象的finalize()就永远不会被调用,因此可能会使对象处于不一致状态。

如果在finalize()中抛出异常,则会被忽略。

由于您无法控制GC何时被调用,因此您不知道何时调用finalize()。有时,您可能正在打印finalize()中的值,但输出从未显示,因为在调用finalize()时,您的程序可能已经终止。

因此,请避免使用它。相反,创建一个名为dispose()的方法,该方法将关闭必要的资源或进行最终日志记录等。 有关此完整帖子的详细信息。我希望这可以澄清。


Stack Overflow 建议您在回答中提供内容,而不仅仅是链接。如果链接失效,您的回答将毫无价值。 - the Tin Man

0

是的,System.gc(); 如果需要的话会触发finalize()方法。 public class TestGarbageCollection {

public static void main(String[] args) {
    while (true) {
        TestClass s = new TestClass();
        s.display();
        System.gc();
    }
}

}

公共类测试类 {

public TestClass() {
    System.out.println("constructor");
}

public void display() {
    System.out.println("display");
}
@Override
public void finalize() {
    System.out.println("destructor");
}

}

这将触发finalize()方法。无论您是否覆盖本地类或对象的finalize方法,都将调用finalize方法。


不是真的!!!是否在垃圾回收期间调用finalize方法取决于JVM。而且根据Java-doc,调用System.gc并不能保证立即调用垃圾收集器以释放内存。这只是建议!!! - Arun Kumar
@Arun Kumar:尽管Oracle System.gc() JavaDocs说:“调用此方法表明Java虚拟机会花费精力运行已被发现被丢弃但其finalize方法尚未运行的对象的finalize方法。当从方法调用返回时,Java虚拟机已经尽最大努力完成了所有未完成的finalization。” 最后一句话非常清楚。而它之前的那句话却让人困惑。我希望他们能够修复这些文档,因为现在我不知道该怎么想了。 - Dreamspace President

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