Java Groovy引起的Perm OOM

昨天发完项目后一切安好,不料晚上烧烤时收到系统告警,登录机器一看大量java.lang.OutOfMemoryError: PermGen space日志。立马想到了groovy的load有问题,于是先将一台机器的内存dump下来后进行了机器重启,dump命令如下

 

sudo -u admin /opt/taobao/java/bin/jmap -dump format=b,file=/home/admin/dump.bin

 

 

今天大早跑到公司来排查问题,首先在测试环境将系统部署起来,通过http_load压测系统,同时使用jstat查看gc情况,很快重现了线上情况,相关执行代码如下:

http_load压测

 

./http_load -proxy 127.0.0.1:80 -seconds 1000 -parallel 10 url 

 

 jstat查看gc情况

 

[admin@aaa bin]$ ./jstat -gcutil 22606 5s 1000    
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00  99.79   0.00  72.34 100.00     35    1.809     3    0.395    2.204
 99.93   0.00  43.89  18.59  80.80     38    1.902     4    2.120    4.021
 86.57   0.00  30.25  29.22  86.10     42    2.066     4    2.120    4.185
 99.98   0.00  32.74  36.08  91.22     46    2.262     4    2.120    4.381
  0.00  99.97  96.74  39.62  96.51     51    2.537     4    2.120    4.656
  0.00   0.00   0.00  35.99 100.00     59    2.978     6    3.054    6.033
  0.00  61.74  81.24  18.23  90.05     65    3.184     6    3.719    6.903
 73.77  61.21 100.00  19.92  94.84     73    3.715     6    3.719    7.434
 68.02   0.00   8.64  24.04  99.41     80    4.201     6    3.719    7.920
 38.48   0.00  40.17  18.58  93.46     86    4.412     8    5.188    9.601
  0.00  99.97  22.78  18.58  98.31     93    4.815     8    5.188   10.004
  0.00  19.53 100.00  19.21  95.70    100    5.164    10    6.574   11.738
  0.00   0.00   0.00  28.45 100.00    108    5.542    12    7.275   12.816
 61.09   0.00   0.00  19.86 100.00    114    5.762    13    7.939   13.701
  0.00   0.00  16.05  18.38  98.39    120    5.926    16   10.517   16.444
  0.00   0.00   0.00  21.27 100.00    126    6.070    20   12.387   18.457
  0.00   0.00   0.00  18.00 100.00    132    6.193    26   15.972   22.166
  0.00   3.98   0.00  15.29 100.00    139    6.317    33   20.212   26.529

 

 通过gc情况发现,Perm区增长很快,并且在每次Full GC后内存占用量也越来越多,到最后无法通过FGC回收到内存,jvm一直FGC,应用不断报java.lang.OutOfMemoryError: PermGen space

 

于是查看groovy脚本执行相关的代码,发现系统在每次执行一个groovy脚本的时候,会先调isModified接口判断脚本是否发生变化,如果变化了则重新parse脚本,否则返回之前parse缓存的Class。由于Perm区是用来存储java类等区域,因此判断是isModified方法每次都会返回true,导致每次模块执行都将parse。于是对isModified方法做了修改,重新压测,发现Perm区的空间增长很忙了,问题得到了初步解决。

 

在排查中想到,如果每次parse后之前parse的class无法被卸载,那岂不是总有一天会OOM了,只不过是时间长短问题而已。于是想是否有办法手动调用方法卸载class,查看了GroovyClassLoader及其父类的发现都是没有此方法的,想想也是,如果能手动卸载class那风险应该是比较大的。

 

在网上查看资料看到有这么一段:

 

 JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):

   - 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
   - 加载该类的ClassLoader已经被GC。
   - 该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法.

 

 

分析了下GroovyClassLoader的代码,发现如果按照上面说的条件来看,加载的类是无法被卸载的。。。这个有点杯具呀,想重写他的classloader,发现涉及的问题更多了,后面需要在更多分析后,,,

 

TODO:

1、深入了解Groovy在java中结合类加载相关的问题

2、java内存分析方法记录备忘

 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值