我们的GlassFish实例每两周会出现一个
我尝试在谷歌上寻找某种性能分析工具来定位错误,并在此SO线程中提到的jmap证明非常有用。从
以下是示例输出:
java.lang.OutOfMemoryError:PermGen space
错误而崩溃。我将PermGen空间增加到512MB,并使用jstat -gc
开始转储内存使用情况。两周后,我得到了以下图表,显示PermGen空间稳步增加(x轴单位为分钟,y轴为KB)。
![Graph of increasing PermGen usage](https://istack.dev59.com/j4LVr.webp)
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
....
那么我该如何查找导致此问题的代码呢?从这篇文章中,我推断我们的Groovy代码在某处动态地创建类。从jmap的转储中,我可以看到大多数死对象/类(?)有相同的parent_loader,尽管我不确定在这种情况下这意味着什么。我不知道接下来该怎么做。
补充说明
对于后来者,值得指出的是,被接受的答案并没有解决问题。它只是通过不存储太多的类信息来延长了重启所需的时间。实际上解决我们问题的是消除生成它的代码。我们使用了验证(根据契约设计)框架OVal,其中可以使用Groovy作为方法和类的注释来编写自定义约束脚本。将注释改为明确规定的Java代码虽然很无聊,但它完成了工作。我怀疑每次检查OVal约束时都会创建一个新的匿名类,并且相关的类数据会导致内存泄漏。