Young GC 500ms到50ms的优化

问题描述:在进行双十一备战的过程中,发现JVMyoung GC的频次很高,同时一次Young Gc的耗时在500ms左右,FullGC的在1-2天触发一次

JVM原配置:-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+UseCMSInitiatingOccupancyOnly  -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=52001 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

优化方向:关注Young GC ,FullGC触发次数可不考虑

(1)、考虑到这个应用是一个WEB应用,同时也是MQ的消费端,应该第一个原因就是MQ的消费会导致大量的临时对象产生,YOUNG GC频繁,同时在接受商品上下架(全站)、收银台对账消息,每个都在3000/TPS的量,峰值能达到10000+。所以第一个优化思路,是将MQ消费应用,与WEB应用分离,减少之间的影响。

优化完,MQ消费应用YOUNGGC频次同之前,WEB应用YOUNGGC2分钟一次,每次GC耗时:50ms

总结:资源隔离,保护线上稀有资源,是针对系统的优化需要关注的点,此时针对于YOUNG GC已到达本身的系统要求,针对于MQ消费者应用,YOUNGGC频次高一些,对于消费应用的延迟来说还是可接受的。但是还是想搞清楚时间消耗在哪里?

(2)YoungGC时间消耗:

   1.Root Scanning        

   2.Object Copy  

GC roots 包含以下引用:                         

    1.所有java线程以及线程栈帧里指向GC堆里的对象的引用;                            

    2.JNI Local & Global;                                                                                           

    3.由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的;      

    4.stack local Java方法的local变量或参数;                                              

    5.其他,包含monitor & finalizable & native stack

排查方法,打印各种日志。

-XX:+PrintGCDetails -Xloggc:/export/Logs/gc.log 打印日志,,

XX:+PrintReferenceGC 打印Reference回收消耗时长

-XX:+PrintGCApplicationStoppedTime -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 打印安全点时间消耗

-XX:+PrintTenuringDistributio 打印年龄分布(注意动态年龄计算)

查看日志后发现,这几个的时间跟500ms都不是一个量级的,所以根本原因不在这里。

后面考虑到没有设置GC回收线程数量,通过DUMP、JCMD命令可以发现GC线程数量远远超过服务器核数,(4C8G服务器配置)

    ①如果用户显示指定了ParallelGCThreads,则使用用户指定的值。
    ②否则,需要根据实际的CPU所能够支持的线程数来计算ParallelGCThreads的值,计算方法见步骤③和步骤④。
    ③如果物理CPU所能够支持线程数小于8,则ParallelGCThreads的值为CPU所支持的线程数。这里的阀值为8,是因为JVM中调用nof_parallel_worker_threads接口所传入的switch_pt的值均为8。
    ④如果物理CPU所能够支持线程数大于8,则ParallelGCThreads的值为8加上一个调整值,调整值的计算方式为:物理CPU所支持的线程数减去8所得值的5/8或者5/16,JVM会根据实际的情况来选择具体是乘以5/8还是5/16。
    比如,在64线程的x86 CPU上,如果用户未指定ParallelGCThreads的值,则默认的计算方式为:ParallelGCThreads = 8 + (64 - 8) * (5/8) = 8 + 35 = 43。

  ParallelGCThreads= 8 + (N - 8) * 5 / 8

手动设置,ParallelGCThreads= 4

YoungGC耗时变成75ms左右。

此时大部分GC耗时已经解决,继续优化,

1.发现安全点时间耗时都是几ms,故排除这个优化点

2.finalReference耗时在 20-40ms左右,开启-XX:+ParallelRefProcEnabled,并行回收,优化到60ms左右

3.打印年龄代分布,可以看到age1  age2差距比较大,应该大多在第一次younggc就可以回收的对象,在eden区又copy到 s0区,减少,copy,可以稍微增加xmn设置大小,(测试了1G 1.5G 2G)根据不同系统可能不同,最终选择2G在频次和耗时之间折中选择。

备注:

AGE年龄优化分析:

Eden移动到Survivor情况:假设机器2 Eden区域是 机器1 的两倍大,其他条件都保持不变。就一般情况来说(Survivor区域中存活的对象比Eden少很多,比如1%),那么机器1 young gc的频率是 机器2 young gc 频率的2倍;那么假设机器1在T时间内GC一次,在GC之后由Eden区域晋升到Survivor的大小为10M(即age=1),那么机器2在2T时间之后发生GC,1T-2T之间生成的对象和机器1类似,GC之后有10M进入Survivor区域,但是0T-1T内最多会剩余10M内存可能会进入到Survivor,但是在经历1T-2T时间之后也有可能导致object已经不存活,如何判断这部分对象有没有存活呢,在机器1在2T的时间点要又要进行一次young gc,那么在0T-1T之前存活的对象也就是age=1的对象将会再次会经历一次young gc,便是了age=2,所有看age=2的年龄段剩余多少就可以了。机器2一次GC之后,由Eden区域进入到Survivor区域中的大约等于10M+机器1中Survivor中(age=2)也就是机器1:age1+age2中的object对象。

-Xmn2048M -XX:ParallelGCThreads=4  -XX:+ParallelRefProcEnabled  -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=15 -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:/export/Logs/gc.log -XX:+PrintReferenceGC -XX:+PrintTenuringDistribution -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=52001 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值