我们的 glassfish 实例每两周关闭一次,有一段时间使用java.lang.OutOfMemoryError: PermGen space
. 我将 PermGen 空间增加到 512MB,并开始使用jstat -gc
. 两周后,我想出了下图,它显示了 PermGen 空间是如何稳定增长的(x 轴上的单位是分钟,y 轴是 KB)。
我尝试在谷歌上搜索某种可以查明错误的分析工具,并且在此处提到的 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
....
那么我怎样才能继续了解更多关于是什么代码导致这种情况的呢?
从这篇文章中,我推断我们的 Groovy 代码在某处动态创建类。从 jmap 的转储中,我可以看到大多数死对象/类(?)具有相同的 parent_loader,尽管我不确定在这种情况下这意味着什么。我不知道如何从这里开始。
附录
对于后来者,值得指出的是,接受的答案并不能解决问题。通过不存储太多类信息,它只是将重新启动之前所需的时间延长了十倍。真正解决我们问题的是摆脱生成它的代码。我们使用了验证(按合同设计)框架OVal,其中可以使用 Groovy 编写自定义约束作为方法和类的注释。在纯 Java 中删除有利于显式前置和后置条件的注释很无聊,但它完成了工作。我怀疑每次检查 OVal 约束时都会创建一个新的匿名类,并且以某种方式关联的类数据导致内存泄漏。