今天看JVM群里有人发了一个GC状况,让人帮忙看优化的,因而我也凑热闹发了出来想让群里的大神们指导优化一下,如下是优化过程记录.java
一开始我贴了下面的两张图tomcat
jstat看GC记录jstat -gcutil pid 1000 20
jvm
jcmd看VM参数(第一次使用这个命令)jcmd pid VM.flags
优化
能够看到YGC了8W屡次,FGC有1100+,相比较另外一个发出来求教的,我这个更糟糕,他的是运行了3天左右 FGC370次spa
而后飞神让我看下运行时间ps -p pid -o etime
日志
个人也是跑了3天左右,感受优化空间很是的大code
又让我拉了JVM配置jinfo -flags pid
(没权限,没执行成功)ps aux | grep pid
orm
发现个人JVM彻底没作过优化,据我本身的印象,就改过PermSize,由于这个OOM过,因此调大了一点。server
而后飞神给了我一份他以前用过的配置JAVA_OPTS="-Xms2g -Xmx2g -Xmn512m -XX:MaxPermSize=256m -server -Xss256k -XX:PermSize=128M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/log/gclog/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/log/jvmdump/jvm.bin -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:+TieredCompilation -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+PrintHeapAtGC
对象
并嘱咐了一句loggc和dumpPath提早mkdir
由于已是周五晚上了,我没有权限直接修改这个配置,因此准备下周一再配上去看效果。
万万没想到,回家路上,笨神出来讲话了,要我看下存活实例
jmap -histo:live pid
因为没有开启GC日志,因而笨神让我开着jstat(飞神提到jstat -gccause pid
能够跟踪gc状况),而后在另外一个窗口执行jmap -histo:live
刚开始没明白,后来才知道原来这个命令能够触发Full GC
能够看到执行了Full GC之后Old区从90%降到了79%,FGC效果不好,说明活对象太多了。
回过头去看jmap实例,发现AtomicInteger
这个类对象特别的多,居然有300多万个实例,已是top2了。
翻看代码没有发现有使用这个类的地方,初步怀疑是依赖的jar包使用的,笨神建议dump用MAT分析一下。
dump命令导出文件jmap -dump:format=b,file=pid.dump pid
检查了一下项目代码后,发现了问题,项目代码就不贴了
项目中有一个统计API调用次数的类使用了AtomicInteger
,在这个类里针对每一个用户都会生成大概六七十个AtomicInteger
实例,每次上报过数据以后只是简单的把值设置为0,致使负责统计的实例一直持有这些AtomicInteger
,并且随着新用户的不断增长,这些实例数量还会持续增加,最终会致使内存溢出。
修改完这个BUG,从新上线后,跑了一段时间查看gc状况
能够看到比以前好一些了 可是FGC的次数仍是比较多,照这状况下去一天的FGC估计会有200+,这固然是不可接受的(前面说的另外一我的370次FGC,飞神说若是是跑了半年的话还能够接受)。
看了飞神推荐的阿里毕玄大师的文章为何不建议
因而准备先不上CMS GC,就简单的把Xms,Xmn和GC日志配置了一下
-Xms2048m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=128m -Xss256k -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/project/delivery_v9/code/logs/gc.log -XX:GCLogFileSize=50m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/project/delivery_v9/tomcat/jvmdump/jvm.dump
运行了一天左右看下对比结果
未配置
已配置
能够看到配置完之后对GC影响仍是挺大的(不论是YGC仍是FGC),固然这也是必然的,毕竟没有配置的机器初始内存比较小,在不断扩容的过程当中会频繁的GC,并且这个时候其实没配置的那台机器内存尚未扩充到上限,在资源充足的状况下,这种动态扩容显然是彻底没有必要的。
配置完的机器虽然GC时间和次数已经降了不少了,可是仍是没达到指望的结果,考虑到这个程序短期的活对象是比较多的,能够经过调全年轻代和老年代的内存占比来减小由于年轻代内存不足致使晋升到老年代的对象。
如今已经离开这个项目了,因此后面就没有再继续优化了,之后再有这方面的实践从新写文章记录。