Java应用程序中已加载类的数量可能发生内存泄漏

我最近开始分析我正在使用VisualVM编写的osgi java应用程序。 我注意到的一件事是,当应用程序开始向客户端发送数据时(通过JMS),加载的类的数量开始以稳定的速率增加。 然而,堆大小和PermGen大小保持不变。 即使在停止发送数据之后,类的数量也永远不会下降。 这是内存泄漏吗? 我认为是这样,因为加载的类必须存储在某个地方,但是即使在我运行应用程序几个小时之后,堆和permgen也不会增加。

有关我的分析应用程序的屏幕截图,请转到此处

你是否在某种程度上动态创建新类?

谢谢你的帮助。 我弄清楚问题是什么。 在我的一个类中,我使用Jaxb创建XML字符串。 在这样做时,JAXB使用reflection来创建一个新类。

JAXBContext context = JAXBContext.newInstance(this.getClass()); 

因此,尽管JAXBContext没有在堆中说出来,但是已经加载了类。

我再次运行我的程序,我看到了正常的高原,正如我所料。

我愿意打赌你的问题与字节码生成有关。

许多库使用CGLib,BCEL,Javasist或Janino在运行时为新类生成字节码,然后从受控类加载器加载它们。 释放这些类的唯一方法是释放对类加载器的所有引用。

由于类加载器由每个类保存,这也意味着您不应该释放对所有类的引用[1]。 您可以通过一个不错的分析器捕获这些(我使用Yourkit – 搜索具有相同保留大小的多个类加载器实例)

一个问题是JVM默认情况下不卸载类(原因是向后兼容性 – 人们认为(错误地)静态初始化程序只执行一次。事实是,每次加载类时它们都会被执行。)启用卸载,你应该通过一些使用以下选项:

 -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled 

(使用JDK 1.5测试)

即使这样,过多的字节码生成也不是一个好主意,所以我建议你查看代码以查找罪魁祸首并缓存生成的类。 频繁违规者是脚本语言,动态代理(包括应用程序服务器生成的代理)或巨大的Hibernate模型(在这种情况下,您可以增加您的permgen)。

也可以看看:

  1. http://blogs.oracle.com/watt/resource/jvm-options-list.html
  2. http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
  3. http://forums.sun.com/thread.jspa?messageID=2833028

您可能会发现一些热点标志可用于理解此行为,如:

  • -XX:+ TraceClassLoading
  • -XX:+ TraceClassUnloading

这是一个很好的参考:

http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

除非我误解,否则我们会在这里查看已加载的类,而不是实例。

当您的代码首次引用一个类时,JVM将使用ClassLoader并从.class文件中获取有关该类的信息。

我不确定它会在什么条件下卸载课程。 当然它永远不应该卸载任何具有静态信息的类。

所以我希望模式大致和你的一样,在应用程序运行的时候,它进入区域并引用新类,因此加载的类的数量会增加。

但是,有两件事对我来说很奇怪:

  1. 它为什么如此线性?
  2. 为什么不是高原?

我希望它会向上趋势,但是在一个不稳定的行中,然后逐渐减少,因为JVM已经加载了程序引用的大多数类。 我的意思是,在大多数应用程序中引用了有限数量的类。

你是否在某种程度上动态创建新类?

我建议通过同一个调试器运行一个更简单的测试应用程序以获得基线情况。 然后你可以考虑实现自己的ClassLoader,它会发出一些调试信息,或者可能有一个工具来报告它。

您需要弄清楚要加载的这些类是什么。

是的,它通常是一个内存泄漏(因为我们没有直接处理内存,它更像是一个类实例泄漏)。 我以前经历过这个过程,通常是一些监听器添加到一个没有自行删除它的旧工具包中。

在较旧的代码中,侦听器关系会导致“侦听器”对象保持不变。 我会看一下较旧的工具包或那些没有通过很多转速的工具包。 在以后的JDK上运行的任何长期存在的库都会知道有关删除“Remove Listener”要求的引用对象。

此外,如果每次重新创建窗口,请在窗口上调用dispose。 如果你不这样做,我认为它们不会消失(实际上在关闭设置时也有处理)。

不要担心Swing或JDK监听器,他们都应该使用引用,所以你应该没问题。

使用Eclipse Memory Analyzer检查重复的类和内存泄漏。 可能会发生同一个类多次加载。

问候, 马库斯