Groovy灵活,但请小心--记一次FullGC分析

       某天下午正在噼里啪啦的写代码时,钉钉群疯狂的发FullGC告警,登陆相关机器,jps -lv | grep 找到PID后,执行 jstat -gccause pid 2000 pid显示如下:

CG的原因是per space已满,再执行jmap -heap pid,输出如下:

很明显,Per space已经用完了,当前使用的jdk版本为:Java(TM) SE Runtime Environment (build 1.6.0_29-b11)JVM参数如下:

永久代最大内存为96M,这个区域存储了class字节码以及一些常量信息,要溢出除非是以下几种情况:

1、该区域设置过小,根本无法装载应用的所需的所有class

2、应用里大量动态生成class,如频繁编译jsp或大量使用动态代理生成许多proxy

3、应用自定义classloader,频繁load class

4、应用大量生成字符串,并调用string.intern()

出问题的服务一个class平均大小在10k左右,96M可以加载9800多个class,JVM6在初始化是会加载2000左右类,应用本身只有99个class[linux递归统计文件数:ls -lR | grep "^-" |wc -l],加上引用的类在4000个左右,再加上一些字符串常量,大约在60M左右,因此不可能是第一个原因,该服务是基于Spring MVC的纯后台服务,只有一个jsp管理页面,不管是Spring的AOP还是其他一些动态代理,都是在程序启动就生成好了的,因此也基本可排除第二个原因,剩下3, 4,在应用的代码层面,没有显示调string.intern(),也没有显示自定义classloader,但无法排除引用的代码里是否有,jmap 打印的信息看,per区的确是占用99%,究竟是什么数据消耗内存呢,jvm既然有命令可以看各个区的消耗,应该有命令可以查看永久带的信息,jmap --help,果然找到一个参数 :

-permstat to print permanent generation statistics

执行该命令,首先打印的是字符串占用的空间,20658 intern Strings occupying 2174792bytes,接下输出满屏的groovy/lang/GroovyClassLoader,该GroovyClassLoader只有3个instance,却有4800多条纪录,也就是说这些相同的classloader在不断load class到虚拟机,直到per区内存消耗完。直觉告诉我,肯定是某个代码static引用了GroovyClassLoader,并不断触发该类load class,问题点已经找到,为了不影响线上业务,先重启服务,接下来去代码里搜关键字:groovy,没有结果,正准备通过maven打印依赖关系【mvndependency:tree】查找线索时,突然想到有个工具servlet里使用groovy动态执行一些java代码,groovy是将脚本动态编译后load到虚拟机执行的,自然会产生许多class,问题应该就是这里了,接下来我们梳理下整个事情来龙去脉:

1 代码里创建了一个static GroovyShell ,GroovyShell持有GroovyClassLoader

2 每次传一些java代码过来,调用GroovyShell.eval(xxx)执行

3  GroovyShell动态编译脚本,再调用GroovyClassLoader load到虚拟机执行。

4 当脚本执行完,此前动态生成class就没有什么用了,但字节码还驻留在per区,并且不会被卸载,随着eval调用次数增多, Per区内存就一点点的被消耗完。

为什么这些无用的字节码没被JVM回收呢,这得从class卸载机制说起,JVM的Per区没有单独的Garbage collector,这个区域只是在老年代FullGC时顺带回收的,一个class只有在满足以下条件时,才能被JVM 卸载

1. 该类所有的实例已经被回收

2. 该类的ClassLoder已经被回收

3. 该类对应的Java.lang.Class对象没有任何对方被引用

我们的代码中,因为GroovyClassLoader被GroovyShell引用,而GroovyShell被应用代码static引用,整个应用运行期间,该引用链一直都在,无法满足条件2,所以即使我们脚本已经执行完,但动态生成的字节码还会一直驻留JVM直到内存溢出。因为java并没有提供卸载class的接口,所以我们只能想办法满足上述3个条件,让JVM在必要时卸载class,解决思路就是要打破上述引用链,方案如下:

1 将GroovyShell由static改为局部变量

2 将GroovyShell放到WeakReference里,既能避免重复创建,又支持JVM卸载class

由于问题代码只是一个运维型工具类,时间上不是很敏感,直接采用第一种方案。在本地测试,以下代码执行循环到6000多次时Per区内存溢出,用Jconsole观察,加载的类直线上升,JVM加:XX:+TraceClassLoading  -XX:+TraceClassUnloading,控制台只打印load lcass,不见unload  class日志,信息如下:

而改成局部变量后,控制台出现unload class日志,程序一直运行直到完成,再无per区溢出。

许多运行在JVM上的脚本语言(如Groovy)为我们带来很多便利,如在不重启服务的情况下,修改服务的运行数据,清空缓存等,但若使用不当,则会带来致命打击,所以使用时请小心谨慎。在java项目中,不管是从工程的可维护性还是运行性能来讲,都不建议大量使用脚本。

Note:Java8去掉Per gen,改为metaspace,并且把stirng常量也挪到heap里,metaspace采用直接内存,会自动调节大小,该区域大小只受限于物理内存,因此不会再有PerGen溢出问题,

扩展阅读:jmap命令详解   JVM老年代  jstat命令  GC cause参数解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容