快速上手jvm调优:GC调优思路及参数设置

本文基于《jvm性能权威指南》,总结了gc调优的相关知识点。然后希望能帮助大家快速上手jvm gc调优。
这里不对垃圾收集的原理进行详细介绍,只与性能调优相关。

一、垃圾收集器

1.各垃圾收集器主要特点
垃圾收集器 主要特点 启用参数
Serial垃圾收集器 Client模式默认垃圾收集器;
单线程回收;
MinorGC和FullGC都会暂停应用线程。
-XX:+UseSerialGC
Parallel垃圾收集器 Server模式默认垃圾收集器(jdk9前);
使用多线程回收;
MinorGC和FullGC都会暂停应用线程,FullGC会对老年代空间进行压缩整理。
-XX:+UseParallelGC,
-XX:+UseParallelOldGC
CMS垃圾收集器 使用多线程回收;
MinorGC和老年代并发回收阶段暂停应用线程,会有空间碎片化问题。
-XX:+UseParNewGC,
-XX:+UseConcMarkSweepGC
G1垃圾收集器 jdk9后默认垃圾收集器;
使用非连续的区块的方式管理堆内存;
多线程并行回收;
MinorGC和老年代并发回收部分阶段暂停应用线程。
-XX:+UseG1GC
2.如何选择垃圾收集器
  • 小内存应用直接使用Serial,稳定高效
  • 大部分应用在Parallel和CMS中选择:
    • Parallel+Parallel old组合主要关注吞吐量,适用批处理任务
    • 在CPU资源充足的情况下使用CMS(CMS只使用老年代,需要配合serial/parallel new)
    • CPU资源受限时CMS会发生并发模式失效的问题,退化到Serial Old
  • 一般情况下,堆空间小于4G时,CMS比G1性能好,但超大堆下G1性能更好

二、GC调优基础

1.分析工具

所有调优都是建立在对堆内存的使用情况有一定的了解的基础上,所以我们首先需要使用一些工具对我们的java应用进行监控和分析,来了解到底出现了什么问题:

  • GC日志
    • -XX:+PrintGCDetails:打印GC日志
    • -XX:+PrintGCTimeStamps:GC日志打印时间戳
  • 堆实时监控
    • 使用jstat -gcutil PID 1000 指令:每秒打印堆内存实时信息及GC情况
2.调整堆大小
  • 考虑维度:
    • GC频率:过频繁时需要增大堆大小
    • 单次GC耗时:耗时过长需要考虑控制堆大小
    • GC在整个时间中所占百分比
    • FullGC后剩余可用空间:普遍经验为70%可用
    • 调优参数:
      • -Xms=N、-Xmx=N:堆初始值和最大值
      • -Xmn=N:设置固定新生代大小
      • -XX:NewRatio=N:设置新生代与老年代空间占用比(此值为分母)
      • -XX:NewSize=N、-XX:MaxNewSize=N:设置新生代初始和最大值
3.永久代和元空间
  • 元空间默认会使用尽可能多的空间,因此不用过于关注
  • 永久代(jdk7及之前)
    • -XX:PermSize=N、-XXMaxPermSize=N:设置固定永久代大小和最大值
  • 元空间(jdk8以后)
    • -XX:MetaspaceSize=N、-XX:MaxMetaspaceSize=N:设置固定元空间大小和最大值
4.控制并发
  • 控制线程数:-XX:ParallelGCThreads=N,影响多线程操作:
    • Parallel 收集新生代和老年代
    • CMS:新生代收集(ParNewGC)、并发收集STW阶段(非FullGC)
    • G1 新生代收集、STW阶段(非FullGC)
  • 默认线程数计算(n为cpu线程数):ParallelGCThreads = 8 + ( ( N-8 ) * 5 / 8 )
    • 当机器CPU线程数比较多,此时默认线程数会比较大,而又同时运行多个jvm实例,这时需要手动控制线程数,避免过多垃圾回收线程并发运行。
5.自适应调整

JVM会自己根据以往的性能历史进行性能参数调整

  • -XX:-UseAdaptiveSizePolicy:关闭自适应调整功能(默认开启)
  • -XX:+PrintAdaptiveSizePolicy:打印自动调整信息

三、进阶调优

1. Parallel GC
  • Parallel收集器会根据指标自适应调整堆大小
    • -XX:MaxGCPauseMillis=N:设置最大停顿时间
      • 倾向于降低老年代大小,可能会触发频繁FullGC
    • -XX:GCTimeRatio=N:设置应用线程运行时间与垃圾回收时间之比(默认99)
      • N = 应用时间占比 / (1 - 应用时间占比 )
      • N=99时,表示GC占总时间1%
      • 倾向于增大堆大小
2. CMS
  • 退化为FullGC的情况:
    • 并发模式失效:新生代发生垃圾回收,而老年代没有足够空间容纳晋升对象
    • 晋升失败:老年代有足够空间容纳晋升对象,但是由于空间碎片化导致晋升失败
    • 永久代空间用尽
  • 控制并发周期启动时机
    • -XX:+UseCMSInitiatingOccupancyOnly:不自动调整CMS垃圾收集周期(默认false)
    • -XX:CMSInitiationOccupancyFraction=N:触发并发收集周期的老年代空间占用比阈值(默认70)
      • 不要将阈值设置得过低,至少要比堆内活跃数据数多10%~20%,频繁的并发周期中的STW会导致总体停顿过多
  • 调整CMS后台线程
    • -XX:ConcGCThreads=N:后台线程数目
      • 默认根据ParallelGCThreads值计算得来:N = ( 3 + ParallelGCThreads ) / 4
3. G1
  • 退化为FullGC的情况:
    • 并发模式失效:(参考cms)
    • 晋升失败:(参考cms)
    • 疏散失败:进行新生代垃圾收集时,Suvivor空间和老年代空间没有足够空间容纳幸存对象
    • 巨型对象分配失败:对象所需内存过大,超过了单个区块内存大小(或其他限制)
  • 调整G1后台线程数
    • -XX:ConcGCThreads=N:后台线程数目
      • 默认根据ParallelGCThreads值计算得来:N = ( 2 + ParallelGCThreads ) / 4
  • 调整垃圾收集频率
    • -XX:InitiationHeapOccupancyPercent=N:触发并发收集周期的堆内存占用比阈值(默认45)
  • 调整混合式垃圾收集周期
    • -XX:G1MixedGCLiveThresholdPercent=N:触发分区回收标记的分区垃圾占比阈值
    • -XX:G1MixedGCCountTarget=N:最大混合式GC周期数(默认8)
      • 减少此值可以帮助解决晋升失败问题,代价是混合式GC周期停顿时间更长
    • -XX:MaxGCPauseMillis=N:设置最大停顿时间(默认200ms)
4. Survivor空间及晋升:
  • 设置Survivor空间大小
    • -XX:InitialSurvivorRatio=N:初始Survivor空间大小占比(N为分母)
      • survivor空间大小 = new_size / ( InitialSurvivorRatio + 2 )
    • -XX:MinSurvivorRatio=N:Survivor最大大小(注意由于N是分母,虽然参数是min,但是却是设置的最大值)
  • 自动调节
    • -XX:TargetSurvivorRatio=N:垃圾回收后空闲空间占比
  • 晋升阈值:对象在Survivor空间之间来回移动多少个GC周期后晋升到老年代
    • -XX:InitialTenuringThreshold=N:初始晋升阈值
    • -XX:MaxTenuringThreshold=N:最大晋升阈值
    • JVM会持续的计算,在1和最大晋升阈值中选择合适的值
发布了35 篇原创文章 · 获赞 104 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/wk52525/article/details/94899432