共计 3626 个字符,预计需要花费 10 分钟才能阅读完成。
送大家以下 Java 学习材料
通过这一个多月的致力,将 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,能够及早发现问题