文档章节

百万连接,百亿吞吐量服务的JVM性能调优实战

BakerZhu
 BakerZhu
发布于 09/19 15:12
字数 1571
阅读 55
收藏 30

转载占小狼博客 应用: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直接报错 见 http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2008-May/000309.html

第二次调优

将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://mp.weixin.qq.com/s?__biz=MzIwMzY1OTU1NQ==&mid=2247484236&idx=1&sn=b9743b2d7436f84e4617ff3...

共有 人打赏支持
BakerZhu
粉丝 98
博文 517
码字总数 422971
作品 0
通州
程序员
私信 提问
成为Java GC专家(5)—Java性能调优原则

这是“成为Java GC专家”系列的第五篇文章。在第一篇深入浅出Java垃圾回收机制中,我们已经学习了不同的GC算法流程、GC的工作原理、新生代(Young Generation)和老年代(Old Generation)的...

stefanzhlg
2014/12/05
0
1
如何合理的规划一次jvm性能调优

这是jvm优化系列第三篇: jvm优化——垃圾回收 jvm优化——监控工具 JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考虑各方面的影响。但也有一些基础的理论和原则,理解...

wier
2017/10/25
0
9
大型互联网架构必备技术——性能调优专题

性能调优 深入内核,直击故障 ,拒绝蒙圈 性能优化如何理解 1、性能基准 2、什么是性能优化 3、衡量标准 JVM调优 1、Jvm虚拟机内存剖析 2、垃圾收集器 3、实战调优案例与解决方案 4、Jvm运行...

Java高级架构
04/15
0
0
Java性能调优工程的几点建议

2016年8月,由极客邦、InfoQ和听云联合主办APMCon 2016 中国应用性能管理大会上,Java性能调优专家Monica Beckwith进行了《Java性能调优必读守则》(原题目:Java Performance Engineer's S...

紫魅编程
2016/10/11
487
3
Java 后端技术栈,到底如何深入学习?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/t4i2b10X4c22nF6A/article/details/83118587 很多人做Java开发4,5年后,都会感觉自己遇到瓶颈。什么都会又什...

JAVA高级架构v
10/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

[LintCode] Linked List Cycle(带环链表)

描述 给定一个链表,判断它是否有环。 样例 给出 -21->10->4->5, tail connects to node index 1,返回 true。 这里解释下,题目的意思,在英文原题中,tail connects to node index 1 表示的...

honeymose
10分钟前
0
0
Android :报错Your project path contains non-ASCII characters.

报错内容如下 Your project path contains non-ASCII characters. This will most likely cause the build to fail on Windows. Please move your project to a different directory. See ht......

lanyu96
21分钟前
2
0
Nginx平滑添加模块

Nginx已经编译安装并运行了一段时间, 然后某一天, 发现需要用到某个模块但当初没有编译, 这个时候怎么办呢? 卸载重新安装肯定可以的, 如果Nginx版本没有变更的话, 则有一个相对平滑的方法来添...

老菜鸟0217
26分钟前
2
0
spark安装测试

spark安装测试 由于本地已经安装好hadoop相关组件,所以本文是在yarn的基础上对spark进行安装及测试 确保hdfs及yarn成功启动,hadoop版本为2.7.3 安装scala,由于本人安装的spark是2.4.0,对应...

-九天-
40分钟前
3
0
周末看完了《电能计量自动化技术》

整体质量还行,下面分别将心得记录如下: 第一章:发展历程可以看看,现在算是智能电网阶段 2:讲主站系统。以文件进行各模块的交互很值得思考,尤其是批量数据,多团队合作的情况下。另外线...

max佩恩
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部