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

时间:2008-10-03 16:26:50

标签: java profiling

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

有关我的性能分析应用程序的屏幕截图,请转到here

6 个答案:

答案 0 :(得分:5)

  

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

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

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

因此虽然JAXBContext没有在堆中说,但是已经加载了类。

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

答案 1 :(得分:4)

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

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

这是一个很好的参考:

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

答案 2 :(得分:4)

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

许多库使用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

答案 3 :(得分:3)

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

当你的代码首次引用一个类时,JVM会让ClassLoader出去并从.class文件中获取有关该类的信息。

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

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

然而,有两件事对我来说很奇怪:

  1. 为什么它如此线性?
  2. 为什么不是高原?
  3. 我希望它会向上趋势,但是在一个不稳定的行中,然后逐渐减少,因为JVM已经加载了程序引用的大多数类。我的意思是,在大多数应用程序中引用了有限数量的类。

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

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

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

答案 4 :(得分:0)

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

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

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

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

答案 5 :(得分:0)

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

此致 Markus

相关问题