本文介绍了了解 Groovy/Grails 类加载器泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

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

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.

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

I googled this class and took a look at it's ultra helpful Javadocs. So I still have no idea what this class does.

但是在谷歌上搜索它也带来了大约十几篇相关文章(其中一些是 SO 问题),涉及这个类和 Grails/Groovy 应用程序的 PermGen/类加载器泄漏.虽然看起来我的应用确实使用 GC 清理了这些 250K 实例,但仍然令人不安的是它的实例太多,并且 GC 锁定 CPU 超过 5 分钟.

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.

我的问题:

  • 这个类是什么,Groovy 用它做什么?
  • 有人可以向我解释这个答案吗?为什么 -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 可以帮助解决这个特定问题?
  • 为什么这个类对 PermGen 来说特别麻烦?
  • 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 是一种动态语言,每个方法调用都是动态调度的.为了优化 Groovy,为 MetaClassRegistry 中的每个 java.lang.Class 创建一个 MetaClass.这些 MetaClass 实例是按需创建的,并使用弱引用进行存储.

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.

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

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.

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

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()
}

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

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.

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

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.

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

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:

定位使用死 Groovy 填充 PermGen 的代码代码

然而,典型的 Grails 应用程序不会在运行时动态编译类,因此调整 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.

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

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

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

09-12 07:56