通过这一个多月的致力,将 FullGC 从 40 次/天优化到近 10 蠢才触发一次,而且 YoungGC 的工夫也缩小了一半以上,这么大的优化,有必要记录一下两头的调优过程。

对于JVM垃圾回收,之前始终都是处于实践阶段,就晓得新生代,老年代的降职关系,这些常识仅够应酬面试应用的。前一段时间,线上服务器的FullGC十分频繁,均匀一天40屡次,而且隔几天就有服务器主动重启了,这表明的服务器的状态曾经十分不失常了,失去这么好的机会,当然要被动申请进行调优了。未调优前的服务器GC数据,FullGC十分频繁。

首先服务器的配置十分个别(2核4G),总共4台服务器集群。每台服务器的FullGC次数和工夫根本差不多。其中JVM几个外围的启动参数为:

-Xms1000M -Xmx1800M -Xmn350M -Xss300K -XX:+DisableExplicitGC -XX:SurvivorRatio=4 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC

-Xmx1800M:设置JVM最大可用内存为1800M。-Xms1000m:设置JVM初始化内存为1000m。此值能够设置与-Xmx雷同,以防止每次垃圾回收实现后JVM从新分配内存。-Xmn350M:设置年老代大小为350M。整个JVM内存大小=年老代大小 + 年轻代大小 + 长久代大小。长久代个别固定大小为64m,所以增大年老代后,将会减小年轻代大小。

此值对系统性能影响较大,Sun官网举荐配置为整个堆的3/8。-Xss300K:设置每个线程的堆栈大小。JDK5.0当前每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具利用的线程所需内存大小进行调整。在雷同物理内存下,减小这个值能生成更多的线程。然而操作系统对一个过程内的线程数还是有限度的,不能有限生成,经验值在3000~5000左右。

第一次优化

一看参数,马上感觉新生代为什么这么小,这么小的话怎么进步吞吐量,而且会导致YoungGC的频繁触发,如上如的新生代收集就耗时830s。初始化堆内存没有和最大堆内存统一,查阅了各种材料都是举荐这两个值设置一样的,能够避免在每次GC后进行内存重新分配。基于后面的常识,于是进行了第一次的线上调优:晋升新生代大小,将初始化堆内存设置为最大内存

-Xmn350M -> -Xmn800M -XX:SurvivorRatio=4 -> -XX:SurvivorRatio=8 -Xms1000m ->-Xms1800m

将SurvivorRatio批改为8的本意是想让垃圾在新生代时尽可能的多被回收掉。就这样将配置部署到线上两台服务器(prod,prod2另外两台不变不便比照)上后,运行了5天后,察看GC后果,YoungGC缩小了一半以上的次数,工夫缩小了400s,然而FullGC的均匀次数减少了41次。YoungGC根本合乎预期构想,然而这个FullGC就齐全不行了。

就这样第一次优化宣告失败。

第二次优化

在优化的过程中,咱们的主管发现了有个对象T在内存中有一万多个实例,而且这些实例占据了将近20M的内存。于是依据这个bean对象的应用,在我的项目中找到了起因:匿名外部类援用导致的,伪代码如下:

public void doSmthing(T t){ redis.addListener(new Listener(){  public void onTimeout(){   if(t.success()){    //执行操作   }  } });}

因为listener在回调后不会进行开释,而且回调是个超时的操作,当某个事件超过了设定的工夫(1分钟)后才会进行回调,这样就导致了T这个对象始终无奈回收,所以内存中会存在这么多对象实例。通过上述的例子发现了存在内存透露后,首先对程序中的error log文件进行排查,首先先解决掉所有的error事件。而后再次公布后,GC操作还是根本不变,尽管解决了一点内存透露问题,然而能够阐明没有解决根本原因,服务器还是持续莫名的重启。

内存透露考察

通过了第一次的调优后发现内存透露的问题,于是大家都开始将进行内存透露的考察,首先排查代码,不过这种效率是蛮低的,根本没发现问题。于是在线上不是很忙碌的时候持续进行dump内存,终于抓到了一个大对象。

这个对象居然有4W多个,而且都是清一色的ByteArrowRow对象,能够确认这些数据是数据库查问或者插入时产生的了。于是又进行一轮代码剖析,在代码剖析的过程中,通过运维的共事发现了在一天的某个时候入口流量翻了好几倍,居然高达83MB/s,通过一番确认,目前齐全没有这么大的业务量,而且也不存在文件上传的性能。征询了阿里云客服也阐明齐全是失常的流量,能够排除攻打的可能。

就在我还在考察入口流量的问题时,另外一个共事找到了基本的起因,原来是在某个条件下,会查问表中所有未解决的指定数据,然而因为查问的时候where条件中少加了模块这个条件,导致查问出的数量达40多万条,而且通过log查看过后的申请和数据,能够判断这个逻辑的确是曾经执行了的,dump出的内存中只有4W多个对象,这个是因为dump时候刚好查问出了这么多个,剩下的还在传输中导致的。而且这也能十分好的解释了为什么服务器会主动重启的起因。

解决了这个问题后,线上服务器运行齐全失常了,应用未调优前的参数,运行了3天左右FullGC只有5次:

第三次调优

内存透露的问题曾经解决了,剩下的就能够持续调优了,通过查看GC log,发现前三次GullGC时,老年代占据的内存还有余30%,却产生了FullGC。于是进行各种材料的考察,在:https://blog.csdn.net/zjwstz/... 博客中十分清晰明了的阐明metaspace导致FullGC的状况,服务器默认的metaspace是21M,在GC log中看到了最大的时候metaspace占据了200M左右,于是进行如下调优,以下别离为prod1和prod2的批改参数,prod3,prod4放弃不变

-Xmn350M -> -Xmn800M -Xms1000M ->1800M -XX:MetaspaceSize=200M -XX:CMSInitiatingOccupancyFraction=75

-Xmn350M -> -Xmn600M -Xms1000M ->1800M -XX:MetaspaceSize=200M -XX:CMSInitiatingOccupancyFraction=75

prod1和2只是新生代大小不一样而已,其余的都统一。到线上运行了10天左右,进行比照:prod1:

prod2:

prod3:

prod4:

比照来说,1,2两台服务器FullGC远远低于3,4两台,而且1,2两台服务器的YounGC比照3,4也缩小了一半左右,而且第一台服务器效率更为显著,除了YoungGC次数缩小,而且吞吐量比多运行了一天的3,4两台的都要多(通过线程启动数量),阐明prod1的吞吐量晋升尤为显著。通过GC的次数和GC的工夫,本次优化宣告胜利,且prod1的配置更优,极大晋升了服务器的吞吐量和升高了GC一半以上的工夫。

prod1中的惟一一次FullGC:

通过GC log上也没看出起因,老年代在cms remark的时候只占据了660M左右,这个应该还不到触发FullGC的条件,而且通过前几次的YoungGC考察,也排除了降职了大内存对象的可能,通过metaspace的大小,也没有达到GC的条件。这个还须要持续考察,有晓得的欢送指出下,这里后行谢过了。

总结

通过这一个多月的调优总结出以下几点:

  • FullGC一天超过一次必定就不失常了
  • 发现FullGC频繁的时候优先考察内存透露问题
  • 内存透露解决后,jvm能够调优的空间就比拟少了,作为学习还能够,否则不要投入太多的工夫
  • 如果发现CPU继续偏高,排除代码问题后能够找运维征询下阿里云客服,这次调查过程中就发现CPU 100%是因为服务器问题导致的,进行服务器迁徙后就失常了。
  • 数据查问的时候也是算作服务器的入口流量的,如果拜访业务没有这么大量,而且没有攻打的问题的话能够往数据库方面考察
  • 有必要时常关注服务器的GC,能够及早发现问题

以上是最近一个多月JVM调优的过程与总结,如有谬误之处欢送斧正。

原文链接:https://blog.csdn.net/cml_blo...

版权申明:本文为CSDN博主「cmlbeliever」的原创文章,遵循CC 4.0 BY-SA版权协定,转载请附上原文出处链接及本申明。

近期热文举荐:

1.1,000+ 道 Java面试题及答案整顿(2021最新版)

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!