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

时间:2011-04-28 08:44:50

标签: java memory-leaks profiling permgen jmap

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

我尝试使用谷歌搜索可以查明错误的某种分析工具,并在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约束时都会创建一个新的匿名类,并且关联的类数据以某种方式导致内存泄漏。

2 个答案:

答案 0 :(得分:3)

我们遇到了类似的问题(崩溃之间一周)。麻烦似乎是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();

答案 1 :(得分:-5)

如果您正在使用Sun JVM将其更改为IBM JVM,我希望它能正常工作:)