何时以及如何将 java 类加载器标记为垃圾收集?

Posted

技术标签:

【中文标题】何时以及如何将 java 类加载器标记为垃圾收集?【英文标题】:When and how is a java classloader marked for garbage collection? 【发布时间】:2011-01-21 15:28:44 【问题描述】:

我们正在创建多个子类加载器,以将多个子应用程序加载到 Java 应用程序“容器”中,对热部署进行原型设计。当特定类加载器的类路径发生更改(即添加、删除、更新 jar)时,旧的类加载器将被丢弃(未引用),并为新的 jar 类路径创建一个新的类加载器。

更新类路径后,触发热部署,我们进行了堆转储。堆转储(使用内存分析器)表明旧的类加载器没有被垃圾收集。父类加载器中的某些类正在缓存旧的类加载器。调用了以下内容来清除这些缓存:

java.lang.ResourceBundle.clearCache(classLoader);
org.apache.commons.logging.LogFactory.release(classLoader);
java.beans.Introspector.flushCaches();

即使在清除了上述缓存之后,旧的类加载器仍然没有被垃圾回收。对类加载器的其余引用包括以下内容:

类加载器加载的类 java.lang.Package 由类加载器自己创建 类加载器自己创建的java.lang.ProtectionDomain

以上都是类加载器中的循环引用,应该会触发垃圾回收。我不确定为什么不是。有谁知道为什么即使使用循环引用,旧的类加载器仍然没有被垃圾收集?

【问题讨论】:

您使用哪个 JVM(确切版本)?您是否使用任何可能影响类加载的 JVM 选项?您是否使用 Sun 自己的实现中的任何东西?应用程序是否操作字节码? ... 什么环境,可能会影响类加载? 与您的主要问题无关,但您是否考虑过类似 OSGi 之类的东西,而不是做自己的支持热部署的框架? @bfoo 在我们的测试中,我们使用的是 Java 6。没有 JVM 选项。在最简单的情况下,我们没有使用 Sun 的 impl 。没有字节码操作。 @stevendick 是的,我们考虑过 OSGi,但对于我们的用例来说,OSGI 似乎有点矫枉过正。要克服的主要障碍是避免不同子应用程序使用的第三方库发生冲突。子应用程序将使用完全等效的库,但在不同的类加载器中,以避免任何潜在的冲突和复杂性。听起来确实会占用太多内存……您使用过 OSGi 吗?如果是这样,您面临的一些障碍是什么? 要清楚,使用 Sun 实现我的意思是例如使用带有序列化的套接字通信,例如 RMI/HTTP/SOAP 调用等。这些实现可能使用缓存方法,例如用于序列化问题。这样,您的类(将被卸载)由上面的 ClassLoader 和/或您希望清除的 ClassLoader 引用。这些实现可能会使用对类的强引用,以确保序列化不会引发系统/VM IO 问题。 【参考方案1】:

我一直听说Classloader 卸载有问题。当没有对对象实例的引用并且不需要卸载类时,它们是theoretically 垃圾收集,但实际上它似乎更有问题。微妙的引用可能会泄漏并阻止Classloader 被回收。在应用服务器中,经过无数次重新部署循环,我有时会得到一个OutOfMemoryError: PermGen space

所有这一切都是说我猜某处有一个讨厌的引用阻止它被收集——也许内存分析器没有正确地跟踪链接。正如这些文章中所述,这一切似乎都可能发生:

Classloader leaks: the dreaded PermGen space exception How to fix the dreaded PermGen space exception

另外,我不知道你在做什么,但如果你可以等待 JDK 7,你可以看看AnonymousClassLoader。将引入它们以更好地支持动态语言,如本文所述:

A first taste of InvokeDynamic

希望对你有帮助。

【讨论】:

一个子应用非常简单。它使用的唯一第三方库是 log4j。没有其他的。其余的是基本的 JDK API。它还使用保留缓存的 ResourceBundle 类。在调用 clearCache() 之后,这些引用确实消失了,但它仍然没有被垃圾收集。但是感谢您的回答:) 哦,顺便说一句,很棒的链接! 我只能建议尝试使用最简单的子应用程序(比如一个类),然后添加越来越多的类和依赖项,看看它在什么时候停止类加载器的 GC。但我知道这可能非常耗时。顺便说一句,如果您有兴趣,Frank Kievet 还写了有关 XA 交易的有趣帖子。 你是对的。一个讨厌的引用阻止它被收集。但是,Eclipse 内存分析器毕竟正确地显示了所有引用。系统的实际架构比我上面描述的要复杂一些,是为了问题的简洁性而编写的。我将在回答中提供更多详细信息。 很高兴听到 GC 中没有错误,而且确实是泄漏 :)

以上是关于何时以及如何将 java 类加载器标记为垃圾收集?的主要内容,如果未能解决你的问题,请参考以下文章

36.JVM内存分哪几个区,每个区的作用是什么如和判断一个对象是否存活java垃圾回收机制垃圾收集的方法有哪些java类加载过程类加载机制双亲委派Minor GC和Major GC

Java 虚拟机

JVM 笔记

JVM 笔记

jvm的结构,已经垃圾回收

Java 虚拟机原理 ——垃圾收集