未为所有垃圾对象调用覆盖方法 finalize() [已关闭]

Overriden method finalize() isn't called for all of the garbage objects [closed]

提问人:KonstantinosTrimikliniotis 提问时间:11/8/2023 更新时间:11/8/2023 访问量:47

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

15天前关闭。

我有一个类 Tank,它有一个被覆盖的方法 finalize(),它打印消息:

@Override
protected void finalize() {
    if (full) {  // -full : boolean
        System.out.println("Error: tank " + id + " must be empty at cleanup"); // -id: int
    }
    else { 
        System.out.println("Tank " + id + " cleaned up OK");  // -id: int
    }
}

然后我创建一个带有 Tanks 的数组并对每个数组进行设置,并在循环中删除每个链接:

for (int i = 0; i < 5; i++) {
    tanks[i] = null;
}
System.gc()

每次运行我都会收到不同的消息组合。我知道我不能确定调用是否带来了垃圾回收器,但是为什么我只对某些对象有消息,而不是对所有对象都有消息?如果垃圾收集器来了,它不会收集所有“死”链接吗?System.gc()

输出示例:

  1. 没有0坦克
Tank 4 cleaned up OK
Tank 3 cleaned up OK
Error: tank 2 must be empty at cleanup
Error: tank 1 must be empty at cleanup
  1. 只有坦克 4:
Tank 4 cleaned up OK
Java 垃圾回收

评论

4赞 teapot418 11/8/2023
System.gc - 建议,尽力而为等。- JVM是否决定在这里做任何事情取决于实现(配置,天气和月相)。
2赞 teapot418 11/8/2023
如果您想深入了解 GC 的内部情况,可以启用详细的垃圾回收日志记录。但要为很多胆量做好准备。
5赞 Holger 11/8/2023
首先,我们不知道您设置的引用是否是对特定对象的最后一个引用。即使是垃圾回收,垃圾回收行为也取决于对象的历史记录、创建时间、上次使用时间等。我们对这种情况一无所知;我们看到的只是代码将数组的前五个元素设置为 。nullnull
3赞 Mark Rotteveel 11/8/2023
鉴于终结器已被弃用,并且可能会在将来的 Java 版本中被删除,您最好不要学习它们的工作原理以及如何使用它们。请改为了解。Cleaner
0赞 Mark Rotteveel 11/8/2023
无论如何,在第一个例子中,坦克0完全有可能被提升为终身一代,而在第二个例子中,坦克0-3被提升为终身一代,而坦克4仍然处于年轻/伊甸一代。或者他们在不同的地区,GC 决定 GC 一个地区,而不是另一个地区,等等。

答:

1赞 aled 11/8/2023 #1

如果垃圾收集器来了,它不会收集所有“死”链接吗?

简短的回答:否

更长的答案:不能保证在垃圾收集时会发生什么。您必须了解现代 JVM 已经实现了几种通常非常复杂的 GC 算法。他们将做什么以及何时做将有很大差异。我们不知道您的特定环境中的 JVM 版本现在使用的是哪一个版本。这些复杂的算法试图避免的一件事是进行完整的垃圾回收,其中大多数未引用的对象都是垃圾回收的。这是因为这种完整的 GC 事件意味着 JVM 需要停止一段时间,以确保一切一致。这些暂停会影响应用程序性能,并可能导致超时或冻结等问题。GC 算法的开发人员非常努力地避免这些问题。因此,您不应该假设 GC 实现将按照您期望的方式工作。

我建议阅读当前 JVM 实现的不同 GC 算法,以更好地了解它们的复杂性。