什么导致JRE 6 JVM代码缓存泄漏?

17

自从切换到JRE 6后,我的服务器的代码缓存使用量(非堆)会不断增长。我的应用程序会在运行时创建大量的类,但是这些类在GC过程中成功卸载。我可以在gc日志中看到这些类被卸载,并且permGen使用保持不变。我在我的代码中特意确保这些类在我使用完它们后被置为孤立状态,因此它们会从permGen正确地被垃圾回收。

然而,代码缓存仍然在增长。只有在切换到JRE 6之后,我才意识到代码缓存的存在。所以我的问题是:

  1. GC是否包括代码缓存?
  2. 什么可能导致代码缓存内存泄漏?
  3. JDK 6在这个领域中是否存在bug?

你尝试过使用 -XX:ReservedCodeCacheSize 运行时标志限制最大大小吗? - serg10
你好。是的,我有这个问题,但它只是推迟了不可避免的结果。我想我正在尝试理解代码缓存的用途以及在您的代码中如何在JVM内存架构的特定区域中引起内存泄漏。所有其他JVM内存段都被垃圾回收并且稳定。只有代码缓存在无限增长,我不知道原因。谢谢。 - Arturo Knight
你是使用应用服务器来运行代码,还是直接运行它? - GaZ
它正在我们自己的应用服务器内运行。一旦我生成了代码,我就创建一个新的类加载器并将其存储 - 在这一点上,我清除旧类加载器的存储,从而允许gc卸载那些旧的生成类。然后,当我需要其中一个对象时,我将新的类加载器传递给Class.forName()。谢谢。 - Arturo Knight
6个回答

1

从这篇博客文章的评论中:http://blogs.oracle.com/jonthecollector/entry/our_collectors

当类被卸载时,代码也会被清除(同样,在方法“失效”时也会被清除,即在编译它们时做出了一些不再成立的假设)

如果我是你,我会运行一个工作负载,获取一个堆转储,并检查您期望被回收的所有类是否都已被回收。


1

我会寻找问题的两个可能位置:泄漏的加载器和大量字符串内部化。

从您的应用程序描述中,我更有可能验证GC是否卸载了旧生成的类。

我不认为jConsole有一种可视化这个问题的方法,但如果您可以获得YourKit的副本,它有一种图形方式来检测上述两个问题。


2
大规模字符串池化会如何干扰代码缓存?字符串存储在永久代中,而代码缓存是一个单独的区域。 - Puneet

1

您可能需要浏览此讨论并向后查看,以了解可能有助于缩小范围的内容: http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2009-January/000530.html

这个涉及JDK5但可能有帮助: http://www.nabble.com/Java-code-cache-memory-td22202283.html

您是用它来编译jsp页面或类似的东西吗? 如果不是,应用程序启动后正在编译什么? 您是否在运行时使用AspectJ进行编织?

知道您在做什么将有助于更好地了解如何提供帮助。

此外,当代码缓存耗尽时,它是否只会停止重新编译,还是jvm会崩溃? 我想是前者。

您是否使用Sun的JDK? 我猜您是,因为我怀疑其他版本没有列为版本6,但问一下也无妨。


谢谢。我正在使用Velocity模板在系统中定期生成和编译Java代码。我们使用的系统是在Excel电子表格中指定系统配置,当用户更改Excel电子表格时,我们根据配置重新生成Java代码并在运行时从系统内部重新编译。因此,我们有多个版本的Java代码。我知道这些版本正在从permGen中进行垃圾回收,因为我可以在gc日志中看到它。JVM崩溃了,但可能是由于JVM参数不佳。是的,我正在使用JDK 6。会查看链接,谢谢。 - Arturo Knight

1

我在想,Java 6更新14版中的新G1垃圾收集器是否能帮到您。您可以尝试使用-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC进行测试,但是根据Jon Masamitsu博客上的评论,如果问题确实是由于代码缓存引起的,它可能无法解决问题。但也许那里的讨论或链接可能会有所帮助。


0

0

是否可以针对有问题的应用程序调用JDK中的jvisualvm?这可能会让您更清楚地了解正在发生的事情。


我使用jconsole,它可以告诉我所有我需要的信息。我知道代码缓存正在无限增长。我正在尝试找出原因,但是没有JVM代码的可视化会告诉我,我猜测是这样吧?感谢您的评论。 - Arturo Knight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接