java - 了解 Groovy/Grails 类加载器泄漏

标签 java grails memory-leaks groovy classloader

昨天我将我的第一个 Grails (2.3.6) 应用程序部署到开发服务器并开始监控它。我刚刚得到一个自动监视器,指出 CPU 已固定在这台机器上,所以我通过 SSH 连接到它。我运行 top 并发现是我的 Java 应用程序的 PID 固定了服务器。我还注意到内存为 40%。几秒钟后,CPU 停止固定,下降到正常水平,内存又下降到大约 20% 的范围内。经典的主要 GC。

在收集时,我进行了堆转储。在 GC 之后,我在 JVisualVM 中打开转储,看到大部分内存分配给 org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry 类。总共有将近 250,000 个这样的实例,占用了大约 25 MB 的内存。

我用谷歌搜索了这个类并查看了它的 ultra helpful Javadocs .所以我仍然不知道这个类是做什么的。

但是用谷歌搜索它也发现了大约十二篇相关文章(其中一些是 SO 问题)涉及这个类和 PermGen/classloader 与 Grails/Groovy 应用程序的泄漏。虽然我的应用程序似乎确实使用 GC 清理了这 250K 个实例,但仍然令人不安的是,它的实例太多,而且 GC 固定 CPU 超过 5 分钟。

我的问题:

  • 这个类是什么?Groovy 用它做什么?
  • 谁能解释一下this answer大部头书?为什么 -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 可以解决这个特殊问题?
  • 为什么这个类对于 PermGen 来说特别麻烦?

最佳答案

Groovy 是一种动态语言,每个方法调用都是动态调度的。为了优化 Groovy 为 MetaClassRegistry 中的每个 java.lang.Class 创建一个 MetaClass。这些 MetaClass 实例是按需创建的,并使用弱引用存储。

你看到很多 org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry 的原因是因为 Groovy 在内存中存储类和方法的映射,以便可以快速调度它们由运行时。根据应用程序的大小,这可能是因为您已经发现了数千个类,因为每个类可能有数十个有时数百个方法。

但是,在 Groovy 和 Grails 中没有“内存泄漏”,您看到的是正常行为。您的应用程序内存不足,可能是因为没有分配足够的内存,这反过来导致 MetaClass 实例被垃圾回收。现在假设你有一个循环:

for(str in strings) {
   println str.toUpperCase()
}

在这种情况下,我们正在调用 String 类的方法。如果内存不足,将会发生的情况是对于循环的每次迭代,MetaClass 将被垃圾收集,然后为下一次迭代重新创建。如您所见,这会显着降低应用程序的速度并导致 CPU 被固定。这种状态通常称为“元类流失”,表示您的应用程序堆内存不足。

如果 Groovy 不是垃圾收集这些 MetaClass 实例那么是的,这将意味着 Groovy 中存在内存泄漏,但 它是垃圾收集这些类的事实是一个迹象表明一切都很好,除了你没有首先分配足够的堆内存这一事实。这并不是说应用程序的另一部分可能存在内存泄漏,它正在耗尽所有可用内存,并且没有足够的空间让 Groovy 正常运行。

至于您提到的另一个答案,添加类卸载和 PermGen 调整实际上不会解决您的内存问题,除非您在运行时动态解析类。 JVM 使用 PermGen 空间来存储动态创建的类。 Groovy 允许您使用 GroovyClassLoader.parseClassGroovyShell.evaluate 在运行时编译类。如果您不断地解析类,那么是的,添加类卸载标志会有所帮助。另见这篇文章:

Locating code that is filling PermGen with dead Groovy code

但是,典型的 Grails 应用程序不会在运行时动态编译类,因此调整 PermGen 和类卸载设置实际上不会有任何效果。

您应该使用 -Xmx 标志验证您是否分配了足够的堆内存,如果没有则分配更多。

关于java - 了解 Groovy/Grails 类加载器泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24169976/

相关文章:

grails - IntelliJ缺少数据源文件?

grails - FormService中的未知类型 'processDefinition'

iphone - 释放的对象使应用程序崩溃——alloc/init 内存管理问题

iOS CPU 事件突然停止

java - Jpa查询从缓存而不是数据库中提取?

java - Hibernate - 打开和关闭 SQL 索引的使用

java - 用 java 降序输出 Map.Entry<Sentence, Integer>

grails - Grails-最低安装要求

java - 如何获取机器的mac地址

java - 是否有一个 junit 扩展来记录每次测试所用的内存