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

oli*_*ren 10 java profiling memory-leaks permgen jmap

我们已经让我们的glassfish实例每两周下降一段时间java.lang.OutOfMemoryError: PermGen space.我将PermGen空间增加到512MB,并将startet转储内存使用量jstat -gc.两周后,我想出了下图,显示了PermGen空间是如何稳定增加的(x轴上的单位是分钟,y轴是KB). 增加PermGen用法的图表

我试着用谷歌搜索某种可以查明错误的分析工具,并在SO上提到一个线程提到jmap,这被证明是非常有帮助的.在倾倒的大约14000行中jmap -permstats $PID,大约包含12500行groovy/lang/GroovyClassLoader$InnerLoader,指向我们自己的Groovy代码或Groovy本身的某种内存泄漏.我必须指出,Groovy构造的相关代码库不到1%.

示例输出如下:

class_loader    classes bytes   parent_loader   alive?  type

<bootstrap> 3811    14830264      null      live    <internal>
0x00007f3aa7e19d20  20  164168  0x00007f3a9607f010  dead    groovy/lang/GroovyClassLoader$InnerLoader@0x00007f3a7afb4120
0x00007f3aa7c850d0  20  164168  0x00007f3a9607f010  dead    groovy/lang/GroovyClassLoader$InnerLoader@0x00007f3a7afb4120
0x00007f3aa5d15128  21  181072  0x00007f3a9607f010  dead    groovy/lang/GroovyClassLoader$InnerLoader@0x00007f3a7afb4120
0x00007f3aad0b40e8  36  189816  0x00007f3a9d31fbf8  dead    org/apache/jasper/servlet/JasperLoader@0x00007f3a7d0caf00
....
Run Code Online (Sandbox Code Playgroud)

那么我该如何进一步了解导致此问题的代码呢?

本文中,我推断我们的Groovy代码是在某处动态创建类.从jmap的转储我可以看到大多数死对象/类(?)都有相同的parent_loader,虽然我不确定这在这个上下文中意味着什么.我不知道怎么从这里开始.

附录

对于后来者来说,值得指出的是,接受的答案并不能解决问题.它只是通过不存储如此多的类信息来延长重启前所需的时间十倍.实际修复我们问题的是摆脱生成它的代码.我们使用验证(按合同设计)框架OVal,其中可以使用Groovy编写自定义约束作为方法和类的注释.在普通Java中删除注释以支持显式的前置条件和后置条件是很无聊的,但它完成了工作.我怀疑每次检查OVal约束时都会创建一个新的匿名类,并且关联的类数据以某种方式导致内存泄漏.

Dan*_*zek 3

我们遇到了类似的问题(两次崩溃之间间隔 1 周)。问题似乎是 Groovy 缓存元方法。我们最终根据此讨论错误报告使用了此代码

GroovyClassLoader loader = new GroovyClassLoader();
Reader reader = new BufferedReader(clob.getCharacterStream());
GroovyCodeSource source = new GroovyCodeSource(reader, name, "xb3.Classifier");
Class<?> groovyClass = loader.parseClass(source);
Object possibleClass = groovyClass.newInstance();
if (expectedType.isAssignableFrom(possibleClass.getClass())) {
    classifiers.put((T) possibleClass, name);
}
reader.close();
// Tell Groovy we don't need any meta
// information about these classes
GroovySystem.getMetaClassRegistry().removeMetaClass(possibleClass.getClass());
// Tell the loader to clear out it's cache,
// this ensures the classes will be GC'd
loader.clearCache();
Run Code Online (Sandbox Code Playgroud)