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

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

我试着用谷歌搜索某种可以查明错误的分析工具,并在SO上提到一个线程提到jmap,这被证明是非常有帮助的.在从jmap -permstats $PID转储的大约14000行中,大约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
....

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

this article开始,我推断我们的Groovy代码是在某处动态创建类的.从jmap的转储中我可以看到大多数死对象/类(?)具有相同的parent_loader,尽管我不确定这在这种情况下意味着什么.我不知道怎么从这里开始.

附录

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

解决方法:

我们遇到了类似的问题(崩溃之间一周).麻烦似乎是Groovy缓存元方法.我们最终使用了基于this discussionbug report的代码

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();
上一篇:JVM探秘:jmap生成内存堆转储快照


下一篇:java – 使用gcore进行的核心转储,jmap转换为hprof文件格式失败,并显示错误消息