博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
百万连接,百亿吞吐量服务的JVM性能调优实战
阅读量:6120 次
发布时间:2019-06-21

本文共 5268 字,大约阅读时间需要 17 分钟。

hot3.png

应用:shark-新美大移动端网络优化(每日接受移动端请求约150亿)

应用特点 :

  1. qps比较高,新生代增长飞快
  2. 用户的连接需要维持一段时间
  3. 单机需要维持海量连接,几十万的级别

以上三个特点导致有大量小对象聚集在old区,高峰期old区域增长非常快,对象在一段时间内肯定会消亡


初始的线上gc的情况如下

对应的jvm参数为

-Xms10g -Xmx10g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=7g -XX:MaxNewSize=7g -XX:SurvivorRatio=8 -XX:MaxDirectMemorySize=4g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70

可以看到新生代为7G(其中Survivor为2*700M),老年代为3G,对象年龄居然只有9(导致new 区域进入到old区域的速度太快,old区域很快被撑满,频繁old gc),考虑到我这边的对象在一段时间内(几分钟)肯定会消亡,于是先进行一次调优尝试

第一次调优

将年龄调成无穷,调大young区

对应的jvm参数为

-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4-XX:MaxTenuringThreshold=30 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection-XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled  -XX:SoftRefLRUPolicyMSPerMB=0-XX:-ReduceInitialCardMarks -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingPermOccupancyFraction=70

结果old区域一直为空,但是yong gc时间拉长许多,平均每次都要0.2s的时间,比之前的new gc多了整整三倍,是无法接受的

关于设置-XX:MaxTenuringThreshold 大于15,在jdk1.7某个版本之前表示是无穷大,之后不管设置成多少,都是15,jdk1.8之后大于15直接报错 见

第二次调优

将XX:MaxTenuringThreshold 调整回来,调整成最大的值15(大于15即对象长命百岁),由于之前cms 在old gc花的时间比较多,所以这里尝试的serial old

对应的jvm参数为

-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC-XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15

发现效果确实比较明显,new gc的时间缩小了两倍左右,并且old gc的时间也从原来的15000ms 缩小到1500ms

第三次调优

仔细研究了一下第二种调优方式的组合,yong区域使用parNew 的方式,old区域使用serial old 的方式,如果在其他条件都相同的条件下使用parNew+cms的方式,old gc的时间会不会大幅度缩短?毕竟cms还是比较先进的收集器,于是分析了一下cms的几个阶段,有两个阶段是stop the world的,一个是初始化标记(intial mark),一个是重复标记(cms remark),重复标记的原因是从cms old gc开始的那一刻到开始清除可能很多对象的状态都产生了变化,所以这个时候需要暂停用户所有的线程,来一次标记再清除

查看gc日志,发现remark的时间比较长,日志上下文如下

重新标记居然用了18s(这一段[1 CMS-remark: 2202742K(3145728K)] 6959670K(9751808K), 18.1769610 secs])!重新标记的时候,old区域的大小是固定的(这里设置成old区域的70%),按理说每次remark的时间应该都差不多才对,但是查了很多cms old gc日志,发现高峰期和低峰期remark的时间相差太大,两者的区别也只有elden区域,因为我这里elden的设置是比较大的,高峰期的时候,一次cms old开始,到remark之间这段时间,用户程序会和gc线程同步执行,到remark的时候,eden区很可能已经有大量对象了,如果remark之前能把eden区域的对象都清理一遍,那remark的对象将会少很多很多对吧?google了一把,发现cms有这么个参数-XX:+CMSScavengeBeforeRemark,这玩意的意思是在remark之前,来一次yong gc,满足我们的要求,加了这个参数之后,对应的jvm参数为

-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC-XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly-XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70

效果如下

发现old gc的时间缩小到原来cms的1/100,窃喜。接下来分析gc日志,来验证我的猜想

可以看到,在remark之前,来一次yong gc,eden区域从50%降到0(总大小为10.8G),这些空间如果不消除,那么cms将会在这些空间上进行非常耗时的标记,最后再看看remark的时间

[1 CMS-remark: 1471831K(2097152K)] 1542365K(14050944K), 0.1264420 secs],降到0.1264420s,和原来相比,整整一百倍的提高。

总结:

最后,对于长连接,push一类的海量服务端应用,16G内存8核心,推荐的JVM参数如下 jdk 1.7 14g->13g

-Xms13g -Xmx13g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4-XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=70 -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled-XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime-XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError

JDK1.8

-Xms13g -Xmx13g -Xss512k -XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:NewSize=11g -XX:MaxNewSize=11g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC-XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0-XX:-ReduceInitialCardMarks -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError

这样可以保证大多数对象在new区域就销毁,并且到了old区,remark之前先yong gc,然后再来一次cms old gc,将old gc控制在毫秒级别

转载于:https://my.oschina.net/LucasZhu/blog/2056232

你可能感兴趣的文章
DEV实现日期时间效果
查看>>
java注解【转】
查看>>
Oracle表分区
查看>>
centos 下安装g++
查看>>
嵌入式,代码调试----GDB扫盲
查看>>
类斐波那契数列的奇妙性质
查看>>
配置设置[Django]引入模版之后报错Requested setting TEMPLATE_DEBUG, but settings are not configured....
查看>>
下一步工作分配
查看>>
Response. AppendHeader使用大全及文件下载.net函数使用注意点(转载)
查看>>
Wait Functions
查看>>
代码描述10313 - Pay the Price
查看>>
jQuery最佳实践
查看>>
centos64i386下apache 403没有权限访问。
查看>>
vb sendmessage 详解1
查看>>
jquery用法大全
查看>>
Groonga 3.0.8 发布,全文搜索引擎
查看>>
PC-BSD 9.2 发布,基于 FreeBSD 9.2
查看>>
网卡驱动程序之框架(一)
查看>>
css斜线
查看>>
Windows phone 8 学习笔记(3) 通信
查看>>