本文介绍了了解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的内存。



我搜索了这个类,看了一下它的。所以我仍然不知道这个班是做什么的。



但使用Google搜索它也引发了大约十几个涉及这个班的相关文章(其中一些是SO问题), PermGen / classloader会泄露Grails / Groovy应用程序。虽然看起来我的应用程序实际上已经用GC清理了这些250K实例,但仍然令人担忧的是,它有太多的实例,并且GC将CPU固定了超过5分钟。



我的问题:


  • 这个类是什么,Groovy用它做什么?

  • 有人可以向我解释吗?为什么 -XX:+ CMSClassUnloadingEnabled -XX:+ CMSPermGenSweepingEnabled 可以帮助解决这个问题?
  • 为什么这个类对PermGen来说特别麻烦?


解决方案

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



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



然而,没有内存泄漏在Groovy和Grails中,你所看到的是正常的行为。您的应用程序内存不足,可能是因为它没有分配足够的内存,这又导致 MetaClass 实例被垃圾收集。现在说例如你有一个循环:

$ p $ for(str in strings){
println str.toUpperCase()

$ / code>

在这种情况下,我们调用字符串类。如果内存不足,将会发生的情况是,对于循环的每次迭代, MetaClass 将被垃圾收集,然后再次重新创建以用于下一次迭代。这可能会大大减慢应用程序的运行速度,导致CPU被锁定,就像您看到的一样。这种状态通常被称为元类流失(metaclass churn),并且是您的应用程序在堆内存中运行较少的标志。


$ b 如果Groovy 不是垃圾收集这些MetaClass实例然后是的,这意味着Groovy会有内存泄漏,但是收集这些类的垃圾收集这一事实表明一切都很好,除了你有首先没有分配足够的堆内存。这并不是说应用程序的另一部分内存泄漏可能会占用所有可用内存,并且不足以让Groovy正常运行。



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

然而,典型的Grails应用程序不会
em>在运行时动态编译类,因此调整PermGen和类卸载设置实际上不会实现任何功能。


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

Yesterday I deployed my first Grails (2.3.6) app to a dev server and began monitoring it. I just got an automated monitor stating that CPU was pinned on this machine, and so I SSHed into it. I ran top and discovered that it was my Java app's PID that was pinning the server. I also noticed memory was at 40%. After a few seconds, the CPU stopped pinning, went down to a normal level, and memory went back down into the ~20% range. Classic major 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. There were almost 250,000 instances of these in total, eating up about 25 MB of memory.

I googled this class and took a look at it's ultra helpful 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. 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.

My questions:

  • What is this class and what is Groovy doing with it?
  • Can someone explain this answer to me? Why would -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled help this particular problem?
  • Why is this class particularly troublesome for the PermGen?

解决方案

Groovy is a dynamic language, every method call is dispatched dynamically. To optimise that Groovy creates a MetaClass for every java.lang.Class in the MetaClassRegistry. These MetaClass instances are created on-demand and stored using Weak references.

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. 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. 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. 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. 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. This can dramatically slow down an application and lead to the CPU being pinned as you have seen. 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. 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.

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 space is used by the JVM to store dynamically created classes. Groovy allows you to compile classes at runtime using GroovyClassLoader.parseClass or GroovyShell.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

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.

You should verify if you have allocated enough heap memory using the -Xmx flag and if not allocate more.

这篇关于了解Groovy / Grails类加载器泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-11 02:19