Java 应用程序中加载的类数中可能存在内存泄漏
Posted
技术标签:
【中文标题】Java 应用程序中加载的类数中可能存在内存泄漏【英文标题】:Possible Memory leak in Number of Loaded classes in Java Application 【发布时间】:2008-10-03 16:26:49 【问题描述】:我最近开始分析我正在使用 VisualVM 编写的 osgi java 应用程序。我注意到的一件事是,当应用程序开始(通过 JMS)向客户端发送数据时,加载的类的数量开始以稳定的速度增加。但是,堆大小和 PermGen 大小保持不变。即使停止发送数据,类的数量也不会下降。这是内存泄漏吗?我认为是这样,因为加载的类必须存储在某个地方,但是即使在我运行应用程序几个小时后,堆和 permgen 也不会增加。
对于我的分析应用程序的屏幕截图,请转到 here
【问题讨论】:
【参考方案1】:您是否正在以某种方式动态创建新类?
感谢您的帮助。我弄清楚了问题所在。在我的一个课程中,我使用 Jaxb 创建了一个 XML 字符串。在此过程中,JAXB 使用反射来创建一个新类。
JAXBContext context = JAXBContext.newInstance(this.getClass());
因此,尽管 JAXBContext 没有在堆中显示,但类已被加载。
我再次运行了我的程序,我看到了一个正常的平台期。
【讨论】:
请问您是如何摆脱新类的创建的? -我正在解决这个问题,我想试试你的解决方案。 -谢谢 “我又运行了我的程序”你做了哪些改变来看到正常的高原?这个答案没有帮助。【参考方案2】:我敢打赌,您的问题与字节码生成有关。
许多库使用 CGLib、BCEL、Javasist 或 Janino 在运行时为新类生成字节码,然后从受控类加载器加载它们。释放这些类的唯一方法是释放对类加载器的所有引用。
由于类加载器由每个类持有,这也意味着您不应该同时释放对所有类的引用 [1]。你可以用一个像样的分析器来捕捉这些(我使用 Yourkit - 搜索具有相同保留大小的多个类加载器实例)
一个问题是 JVM 默认不卸载类(原因是向后兼容性 - 人们(错误地)假设静态初始化器只会执行一次。事实是每次加载类时它们都会执行.) 要启用卸载,您应该传递一些使用以下选项:
-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled
(使用 JDK 1.5 测试)
即便如此,过多的字节码生成也不是一个好主意,所以我建议你在你的代码中查找罪魁祸首并缓存生成的类。常见的违规者是脚本语言、动态代理(包括由应用服务器生成的代理)或巨大的 Hibernate 模型(在这种情况下,您只需增加 permgen)。
另见:
-
http://blogs.oracle.com/watt/resource/jvm-options-list.html
http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
http://forums.sun.com/thread.jspa?messageID=2833028
【讨论】:
甚至 JVM 也会为 java.lang.reflect.Proxy 生成类并预热 Field.get/set、Method.invoke 和 Constructor.newInstance (IIRC)。 是的,你是对的,这些生成的类仍然缓存在反射层中,所以不应该成为 permgen 泄漏的原因。【参考方案3】:您可能会发现一些热点标志可用于理解此行为,例如:
-XX:+TraceClassLoading -XX:+TraceClassUnloading这是一个很好的参考:
http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
【讨论】:
【参考方案4】:除非我理解错了,否则我们在这里查看的是加载的类,而不是实例。
当您的代码第一次引用一个类时,JVM 让 ClassLoader 出去并从 .class 文件或类似文件中获取有关该类的信息。
我不确定它会在什么条件下卸载一个类。当然,它不应该卸载任何带有静态信息的类。
所以我希望有一个与您的模式大致相似的模式,当您的应用程序运行时,它会进入区域并引用新类,因此加载的类的数量会越来越多。
但是,我觉得有两件事很奇怪:
-
为什么它如此线性?
为什么不平稳?
我预计它会呈上升趋势,但在一条不稳定的线中,然后随着 JVM 已经加载您的程序引用的大部分类而逐渐减少。我的意思是,在大多数应用程序中引用的类数量是有限的。
您是否正在以某种方式动态创建新类?
我建议通过同一个调试器运行一个更简单的测试应用程序来获得基线案例。然后你可以考虑实现你自己的 ClassLoader,它会吐出一些调试信息,或者有一个工具可以让它报告。
你需要弄清楚这些正在加载的类是什么。
【讨论】:
【参考方案5】:是的,这通常是内存泄漏(因为我们并没有真正直接处理内存,所以它更像是类实例泄漏)。我以前经历过这个过程,通常是一些监听器添加到旧工具包中,但不会自行删除。
在旧代码中,侦听器关系会导致“侦听器”对象保留。我会查看较旧的工具包或未经过多次修订的工具包。在更高版本的 JDK 上运行的任何长期存在的库都会知道引用对象,从而消除了“删除侦听器”的要求。
此外,如果您每次都重新创建它们,请在 Windows 上调用 dispose。如果您不这样做,我认为它们永远不会消失(实际上也有关闭设置的处置)。
不要担心 Swing 或 JDK 侦听器,它们都应该使用引用,所以你应该没问题。
【讨论】:
【参考方案6】:使用Eclipse Memory Analyzer 检查重复的类和内存泄漏。同一个类可能会被多次加载。
问候, Markus
【讨论】:
以上是关于Java 应用程序中加载的类数中可能存在内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章