问题描述:早上去公司,突然邮件不断报警,界面报异常,然后查看服务器运行状态,发现javacpu是爆裂。然后开始Troubleshooting和问题解决流程:1.首先在服务器(centos7)上,使用top和uptime命令,发现java的CPU已经爆发,超过100%,导致无法提供后续服务通常情况下;2.调整负载均衡,3.使用jps找到正在运行的tomcat的pid,假设为10086;4、使用jstat-gcutil1008650010(意思是对于pid为10086的线程,每隔500ms显示每一代的内存使用情况),这里是jvm的一些参数设置,如下:可以看到ParNew收集器用于新生代,老年代使用CMS收集器。CMSInitiatingOccupancyFraction=80表示当使用Garbagecollection时,当rate超过80%时触发。然后发现线上老年代的使用率已经超过80%,不到一秒就几乎进行了一次FGC。如此频繁的FGC导致服务无法正常运行;5.使用jmap-histo查看对象数量最多,如果参数是-histo:live,会显示一次FGC后使用次数最多的当前实例。查了一下,发现有大量的Node节点实例使用了ConcurrentHashMap,于是查找代码中使用ConcurrentHashMap的地方。有几个地方经常使用ConcurrentHashMap。看了代码,可能是图片上传下载的问题,但无法确定。6.使用jmap-dump:format=b,file=/usr/local/tomcat/dump1拉下内存情况。如下:文件生成后,将dump1放入eclipse的mat中进行分析。它直接显示了疑似内存泄漏问题。然后分析dump文件给出的信息,找到一个叫IdleConnectionReaper的类。dump文件中提到的内存泄漏的大致意思是IdleConnectionReaper类中的ArrayList存储的东西太多而爆炸。如下:从oss的jar包中找到这个类后,简单看下这个类的组成,如下:搜索了一些官方资料,看了源码,发现这是oss的一个守护线程,就是用于检测上传或下载的工作线程每60秒检查一次空闲的工作线程并回收。然后里面有一个静态的ArrayList,里面存放的是ossClient的链接,默认是1024。7.所以大概找到原因了,就是ossClient的链接太多了,我撑不住了,所以我一直在进行FGC,导致服务不可用。最后找到相关代码,发现有一个小方法,在每次上传或者下载的时候,都会创建一个ossClient。修改代码,将ossClient的调用地改为单个实例。修改后上网跑了一段时间,后来就没有这个问题了。总结:1.对于大量的请求,注意调用的地方是否会造成大量的内存消耗。尽可能使用池化技术、单例等,减少创建和销毁的系统开销;2、CMS的几个缺点,可以参考《深入java虚拟机》,CPU占用会比较高,无法处理浮动垃圾,CMS采用mark-sweep算法,会造成大量空间碎片。如果碎片太多,大对象分配起来就会很困难,所以我们不得不进行FGC,也可能是本文提到的FGC问题的原因。解决方法:-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启老年代的压缩(内存碎片会进行碎片整理,默认开启);-XX:CMSFullGCsBeforeCompaction=0:当启用上面的配置时,这里设置FullGC后多少次,压缩老年代会使FGC时间变长,但是可以提高内存空间的利用率,更容易分配没有多个FGC的大对象。
