jvm的性能调优
常规的参数设置:
-Xms2048m -Xmx2048m -Xmm700m
按照jvm规范,一般年轻代:老年代是 1:2
年轻代中s:e为1:8.
尽量将堆大小设置为固定值,不要使用浮动扩容。
比如:
-Xms2048m -Xmx4096m
还有就是,堆大小不是越大越好,如果堆内存太大,到时候一次full gc可能需要好久才触发一次,这样每次full gc时将会造成很大的停顿时间。
同理,堆大小也不能太小,太小的化,频繁的GC操作,会造成CPU的高负荷,停顿次数增多。
所以,需要找一个平衡点,还有根据应用类型,选择不同的GC回收器。
对于wb应用来讲,最优的组合回收器是:CMS+ParNew,同时适当将年轻代调整大,比如我们应用年轻代:老年代=1:1
参数配置:
-XX:+UseConcMarkSweepGC
使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。
使用CMS时,可能会遇到俩种问题,我们还需要调整参数
promotion failed :一个新的大对象,年轻代放不下去,要直接放入老年代时,老年代也放不下去。这时候老年代收回器将变为串行回收器Serial Old,会导致web应用无响应。(stw问题)
concurrent mode failure :在执行CMS并发GC的时候,有新的对象要进来,这时候空间不足导致的。
对于第一种问题,可能会引起第二种问题的产生。
解决第一种问题的常规方案:年轻代调整大一点(原先放不下的对象,这会可以放下了),比如我们的web项目年轻代:老年代是 1:1。或者(老年代可用空间充足)老年代进行多次CMS后执行一次标记整理,消除内存碎片,或者将CMS触发的阈值从68%调小点。
下面的参数是:5次cms标记清除后,执行一次标记整理压缩
-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5
下面参数是调整阈值,old区内存60%的时候就进行CMS垃圾回收
XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly
对于第二种问题,解决的方案就是:调大老年代的空间。
CMS的六个阶段:
initial-mark 初始标记(CMS的第一个STW阶段),标记GC Root直接引用的对象,GC Root直接引用的对象不多,所以很快。
concurrent-mark 并发标记阶段,由第一阶段标记过的对象出发,所有可达的对象都在本阶段标记。
concurrent-preclean 并发预清理阶段,也是一个并发执行的阶段。在本阶段,会查找前一阶段执行过程中,从新生代晋升或新分配或被更新的对象。通过并发地重新扫描这些对象,预清理阶段可以减少下一个stop-the-world 重新标记阶段的工作量。
concurrent-abortable-preclean 并发可中止的预清理阶段。这个阶段其实跟上一个阶段做的东西一样,也是为了减少下一个STW重新标记阶段的工作量。增加这一阶段是为了让我们可以控制这个阶段的结束时机,比如扫描多长时间(默认5秒)或者Eden区使用占比达到期望比例(默认50%)就结束本阶段。
remark 重标记阶段(CMS的第二个STW阶段),暂停所有用户线程,从GC Root开始重新扫描整堆,标记存活的对象。需要注意的是,虽然CMS只回收老年代的垃圾对象,但是这个阶段依然需要扫描新生代,因为很多GC Root都在新生代,而这些GC Root指向的对象又在老年代,这称为“跨代引用”。
concurrent-sweep ,并发清理。
concurrent-abortable-preclean阶段的时间,如果调整低了之后,我们做remark的时候时间相对就会变长,所以为了达到一个平衡,一般我们还会进行如下修改:
-XX:+CMSScavengeBeforeRemark
在remark阶段之前进行一次Minor GC,这样我们的remark阶段时间可以降低。