了解Groovy / Grails类加载器泄漏
昨天,我将第一个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的内存。
我在该课程上进行了搜索,并查看了它非常有用的Javadocs。所以我仍然不知道这堂课是做什么的。
但是,使用谷歌搜索还提出了涉及该类的十几篇相关文章(其中有些是SO问题),以及有关Grails / Groovy应用程序的PermGen /
classloader泄漏。尽管我的应用程序实际上确实使用GC清理了这250K实例,但仍然令人困扰的是它的实例太多,而且GC将CPU固定了5分钟以上。
我的问题:
- 此类是什么课,Groovy对此做了什么?
- 有人可以向我解释这个答案吗?为什么会
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
帮助解决这个特定问题? - 为什么该类对PermGen特别麻烦?
回答:
Groovy是一种动态语言,每个方法调用都是动态调度的。以优化Groovy中创建一个MetaClass
为每个java.lang.Class
中MetaClassRegistry
。这些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.parseClass
或编译类GroovyShell.evaluate
。如果您要连续解析类,那么可以添加类卸载标志会有所帮助。
但是,典型的Grails应用程序 不会 在运行时动态编译类,因此调整PermGen和类卸载设置实际上并不会实现任何目的。
您应该使用-Xmx标志验证是否分配了足够的堆内存,如果没有分配更多的堆内存,则应使用-Xmx标志。
以上是 了解Groovy / Grails类加载器泄漏 的全部内容, 来源链接: utcz.com/qa/404290.html