5

“已发布”是指没有对剩余类加载器的引用。

我们遇到了一个问题,经常重新部署的 Java EE 应用程序会占用 permgen 空间。分析表明,Java EE 应用程序中的单例已经传递了对应用程序外部应用程序类加载器对象的引用(违反了 Java EE 规则),并且在取消部署应用程序时没有清除它们。

假设没有其他对单例或类对象的引用,当其类的类加载器被释放时,单例的 finalize() 是否会被调用? 我想清除那里的流氓入站引用。或者我是否在一个 catch-22 中,直到类加载器本身可以被垃圾收集之前,finalize 才会被调用——因此永远不会因为流氓外部引用而被调用?

这里的主要问题可能是:

在这种情况下,类对象的类加载器还不能被垃圾收集吗? 这可能取决于类加载器行为的规范,或者可能取决于实现。

参考(另一种!;-))将不胜感激,但不是必需的。

4

2 回答 2

4

如果类加载器有资格进行 GC,并且没有其他对它的引用,则具有对其静态引用的类将有资格进行垃圾收集。

当且仅当垃圾收集器可以回收其定义的类加载器时,才能卸载类或接口。

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7

此外,每个类都有对其类加载器的引用。因此,只要有对由它加载的类的引用或来自不可收集对象的这些类的对象,类加载器就没有资格进行 GC。

终结器在对象符合垃圾回收条件后和实际 GC 发生之前的一段时间运行。

在终结器中释放传入引用(防止 GC)的方法不起作用。只要存在此类引用,就不会调用终结器,因为它们会阻止对象符合垃圾回收的条件。例如,您不能从内部破坏此引用链:

singleton instance <--- singleton class <--- class loader <--
<-- any class loaded by that class loader  <-- any object of such a class
<-- object loaded by another classloader referencing such an object or class
于 2012-02-28T21:33:56.287 回答
1

finalisers 的行为并没有改变类加载器或 perm gen 的使用,尽管它确实使性能问题变得更糟。在运行终结器之前无法收集对象。因此(通过假设非并发来简化,并忽略弱/软/幻像)运行 GC 确定没有对对象图的活动引用,包括类加载器。终结者被添加到终结者队列中,然后被执行。然后在GC的一些后续通过之后,可以恢复内存。这需要一个完整的 GC,包括 perm gen 空间,这通常不常见并且可以禁用。

无论如何,不​​要使用(有状态的)单例。

于 2012-02-28T21:23:29.920 回答