简体   繁体   English

了解Groovy / Grails类加载器泄漏

[英]Understanding Groovy/Grails classloader leak

Yesterday I deployed my first Grails (2.3.6) app to a dev server and began monitoring it. 昨天我将我的第一个Grails(2.3.6)应用程序部署到开发服务器并开始监控它。 I just got an automated monitor stating that CPU was pinned on this machine, and so I SSHed into it. 我刚刚得到一个自动监视器,说明CPU被固定在这台机器上,所以我连接到它。 I ran top and discovered that it was my Java app's PID that was pinning the server. 我跑了top ,发现是我的Java应用程序的PID固定服务器。 I also noticed memory was at 40%. 我也注意到内存是40%。 After a few seconds, the CPU stopped pinning, went down to a normal level, and memory went back down into the ~20% range. 几秒钟后,CPU停止固定,降至正常水平,内存恢复到~20%范围内。 Classic major GC. 经典主要GC。

While it was collecting, I did a heap dump. 在收集的同时,我做了堆转储。 After the GC, I then opened the dump in JVisualVM and saw that most of the memory was being allocated for an org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry class. 在GC之后,我在JVisualVM中打开了转储,看到大部分内存都是为org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry类分配的。 There were almost 250,000 instances of these in total, eating up about 25 MB of memory. 总共有近250,000个这样的实例,占用了大约25 MB的内存。

I googled this class and took a look at it's ultra helpful Javadocs . 我用Google搜索了这个课程并看了一下它是非常有帮助的Javadocs So I still have no idea what this class does. 所以我仍然不知道这门课做了什么。

But googling it also brought up about a dozen or so related articles (some of them SO questions) involving this class and a PermGen/classloader leak with Grails/Groovy apps. 但谷歌搜索它也带来了大约十几篇涉及这个类的相关文章(其中一些是SO问题)和Grails / Groovy应用程序的PermGen / classloader泄漏。 And while it seems that my app did in fact clean up these 250K instance with a GC, it still is troubling that there were so many instances of it, and that the GC pinned CPU for over 5 minutes. 虽然看起来我的应用程序确实用GC清理了这些250K实例,但仍然令人不安的是它有如此多的实例,并且GC将CPU固定超过5分钟。

My questions: 我的问题:

  • What is this class and what is Groovy doing with it? 什么是这个类,Groovy用它做什么?
  • Can someone explain this answer to me? 有人可以向我解释这个答案吗? Why would -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled help this particular problem? 为什么-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled帮助解决这个特殊问题?
  • Why is this class particularly troublesome for the PermGen? 为什么这个课程对PermGen特别麻烦?

Groovy is a dynamic language, every method call is dispatched dynamically. Groovy是一种动态语言,每个方法调用都是动态调度的。 To optimise that Groovy creates a MetaClass for every java.lang.Class in the MetaClassRegistry . 为了优化Groovy,为MetaClass中的每个java.lang.Class创建一个MetaClassRegistry These MetaClass instances are created on-demand and stored using Weak references. 这些MetaClass实例是按需创建的,并使用弱引用存储。

The reason you see a lot of org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry is because Groovy is storing a map of classes and methods in memory so that they can be quickly dispatched by the runtime. 你看到很多org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry的原因是因为Groovy在内存中存储了类和方法的映射,以便运行时可以快速调度它们。 Depending on the size of the application this can be as you have discovered thousands of classes as each class can have dozens sometimes hundreds of methods. 根据应用程序的大小,这可能是因为您已经发现了数千个类,因为每个类可能有几十个,有时有数百个方法。

However, there is no "memory leak" in Groovy and Grails, what you are seeing is normal behaviour. 但是,Groovy和Grails中没有“内存泄漏”,你看到的是正常行为。 Your application is running low on memory, probably because it hasn't been allocated enough memory, this in turn causes MetaClass instances to be garbage collected. 您的应用程序内存不足,可能是因为它没有分配足够的内存,这反过来会导致MetaClass实例被垃圾回收。 Now say for example you have a loop: 现在说比如你有一个循环:

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

In this case we are calling a method on the String class. 在这种情况下,我们在String类上调用一个方法。 If you are running low on memory what will happen is that for each iteration of the loop the MetaClass will be garbage collected and then recreated again for the next iteration. 如果内存不足,将会发生的情况是,对于循环的每次迭代, MetaClass将被垃圾收集,然后再次重新创建以用于下一次迭代。 This can dramatically slow down an application and lead to the CPU being pinned as you have seen. 这可能会大大减慢应用程序的速度,并导致CPU被固定,如您所见。 This state is commonly referred to as "metaclass churn" and is a sign your application is running low on heap memory. 此状态通常称为“元类流失”,是应用程序在堆内存上运行不足的标志。

If Groovy was not garbage collecting these MetaClass instances then yes that would mean there is a memory leak in Groovy, but the fact that it is garbage collecting these classes is a sign that all is well, except for the fact that you have not allocated enough heap memory in the first place. 如果Groovy 没有垃圾收集这些MetaClass实例,那么是的,这意味着Groovy中存在内存泄漏,但事实上它是垃圾收集这些类,这表明一切都很好,除了你没有足够的分配堆内存首先。 That is not to say that there may be a memory leak in another part of the application that is eating up all the available memory and leaving not enough for Groovy to operate correctly. 这并不是说应用程序的另一部分可能存在内存泄漏,这会占用所有可用内存,并且不足以让Groovy正常运行。

As for the other answer you refer to, adding class unloading and PermGen tweaks won't actually do anything to resolve your memory issues unless you dynamically parsing classes at runtime. 至于你提到的另一个答案,添加类卸载和PermGen调整实际上不会做任何事情来解决你的内存问题, 除非你在运行时动态解析类。 PermGen space is used by the JVM to store dynamically created classes. JVM使用PermGen空间来存储动态创建的类。 Groovy allows you to compile classes at runtime using GroovyClassLoader.parseClass or GroovyShell.evaluate . Groovy允许您使用GroovyClassLoader.parseClassGroovyShell.evaluate在运行时编译类。 If you are continuously parsing classes then yes adding class unloading flags can help. 如果你不断解析类,那么添加类卸载标志可以提供帮助。 See also this post: 另见这篇文章:

Locating code that is filling PermGen with dead Groovy code 使用死Groovy代码定位填充PermGen的代码

However, a typical Grails application does not dynamically compile classes at runtime and hence tweaking PermGen and class unloading settings won't actually achieve anything. 但是,典型的Grails应用程序不会在运行时动态编译类,因此调整PermGen和类卸载设置实际上不会实现任何目的。

You should verify if you have allocated enough heap memory using the -Xmx flag and if not allocate more. 您应该使用-Xmx标志验证是否已分配了足够的堆内存,如果没有分配更多内存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM