零、本文纲要
一、 GC调优基本思路
二、 新生代内存调优
三、 老年代内存调优
四、 GC调优案例
- 查看虚拟机运行参数
命令:"C:\Program Files\Java\jdk1.8.0_311\bin\java.exe" -XX:+PrintFlagsFinal -version | findstr "GC"
部分JDK8的JVM默认配置如下:
uintx GCTimeRatio = 99 {product}
uintx MaxGCPauseMillis = 4294967295 {product}
uintx ParallelGCThreads = 13 {product}
bool ScavengeBeforeFullGC = true {product}
bool UseParallelGC := true {product}
bool UseParallelOldGC = true {product}
一、 GC调优基本思路
1. 调优领域
内存
锁竞争
CPU占用
I/O
2. 目标确定
低延迟/高吞吐
低延迟:CMS、G1、ZGC
高吞吐:ParallelGC
3. 减少GC
查看Full GC前后的内存占用
- ① 数据量是不是太多?
数据库查询limit
- ② 数据表示是否太臃肿?
对象图
数据类型选择
- ③ 是否存在内存泄漏?
软引用、弱引用
第三方缓存实现
二、 新生代内存调优
1. 新生代特点
① new操作内存分配廉价(TLAB thread-local allocation buffer)
② 新生代垃圾回收采用复制算法,回收代价低
③ 大部分对象用过即可回收
④ Minor GC的时间远低于Full GC
2. 新生代调优方法
- ① 内存大小调整
命令:-Xmn size
Oracle建议新生代大小占整个堆空间25%-50%。
- ② 新生代调优参考
Ⅰ 新生代能容纳所有[并发量 * (请求 - 响应)]的数据
Ⅱ 幸存区大到能保留[当前活跃对象 + 需要晋升对象]的数据
Ⅲ 晋升阈值配置得当,让长时间存货对象尽快晋升
-XX:+PrintTenuringDistribution
:通过打印幸存区年龄空间数据,预估合理阈值
-XX:MaxTenuringThreshold=threshold
:根据上述阈值,设置使用
三、 老年代内存调优
1. CMS老年代调优
注意:以CMS为例
- ① CMS老年代内存越大越好
避免浮动垃圾过多,进而引起并发清除失败,退化为SerialOld。
-
② 如果Full GC频繁,则先尝试调优新生代
-
③ 观察发生Full GC时老年代内存占用,将老年代内存预设调大1/4~1/3
命令:-XX:CMSInitiatingOccupancyFraction=percent
:当老年代内存使用占到老年代内存空间的68%(默认)触发Full GC。可以设置为75-80%。
四、 GC调优案例
案例一、 Full GC和Minor GC频繁
① 增大新生代内存大小
② 增大幸存区空间,以及晋升阈值
案例二、 请求高峰期发生Full GC,单次暂停时间特别长(CMS)
- ① 确认哪个阶段时间较长:初始标记、并发标记、重新标记、并发清除
Ⅰ 初始标记:STW,标记GC Roots的直接关联对象
Ⅱ 并发标记:没有STW,使用GC Roots Tracing算法,进行跟踪标记。并发标记过程中产生变动的对象会放入一个队列中,供重新标记过程遍历使用。
Ⅲ 重新标记:STW,由于并发标记其他线程不暂停,可能产生新垃圾,重新标记
Ⅳ 标记清除:没有STW
- ② 响应时间优先优化
命令:-XX:+CMSScavengeBeforeRemark
在重新标记之前,进行一次新生代的垃圾回收。可以减少重新标记的对象,提升效率,优化响应。
案例三、 老年代充裕情况下,发生Full GC(1.7)
永久代(元空间)空间不足导致的Full GC
五、结尾
以上即为JVM-GC调优的基础内容,感谢阅读。
网友评论