JVM 看懂GC日志

1. 理解GC日志

阅读GC日志是处理Java虚拟机问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。每种收集器的日志形式都是由塔门自身的实现决定的,换言之,每种收集器的日志格式可能都不同,但虚拟机设计者为方便用户阅读,将各个收集器的日志都维持一定的共性,例如以下经典GC日志。

  • 怎么查看GC日志,在IdeaApplication设置中添加VM options参数-XX:+PrintGCDetails
// VM options = -XX:+PrintGCDetails
public class GCTest {
    public static void main(String[] args) throws InterruptedException {
        System.gc();
    }
}

//输出
[GC (System.gc()) [PSYoungGen: 5243K->791K(76288K)] 5243K->799K(251392K), 0.0013710 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 791K->0K(76288K)] [ParOldGen: 8K->672K(175104K)] 799K->672K(251392K), [Metaspace: 2958K->2958K(1056768K)], 0.0065557 secs] [Times: user=0.03 sys=0.01, real=0.00 secs]

[GC[Full GC说明了这次垃圾回收的停顿类型,而不是用来区分新生代或者是老年代的GC。如果有Full,说明这次GC是发生了 Stop-The-World。接下来的[PSYoungGen 表示GC发生的区域,这里显示的区域名称和所使用的GC收集器相关,比如Serial收集器新生代名字就叫DefNew,ParNew收集器,新生代名是ParNewParallel Scavenge收集器,新生代名字为PSYoungGen,我默认用的是Parallel Scavenge收集器,老年代和新生代同理,名称也是由收集器决定的。后面括号5243K->791K(76288K) 表示年轻代GC前容量->年轻代GC后容量(年轻代总容量),而括号后的5243K->799K(251392K)表示Java堆GC前容量->Java堆GC后容量(Java堆总容量),再往后0.0013710 secs表示GC花费时间,单位秒。有的收集器会给出更准确时间比如[Times: user=0.01 sys=0.00, real=0.00 secs],user、sys、real与Linux 的 Time 命令所输出的时间含义一致,分别代表用户态消耗的CUP时间,内核态消耗CPU时间和操作从开始到结束所经过的墙钟时间(Wall Clock TIme)。墙钟时间包含各种非运算等待时间,例如磁盘I/O,等待线程阻塞等,而CPU时间不包括这些,但当多CUP或者多核的话,多线程操作会叠加这些时间,所以 user 或者 sys 超过 real 时间是正常的。

  • 下表为VM options 参数

参数

描述

UseSerialGC

虚拟机运行在 Client 模式下的默认值,打开此开关后,使用Serial + Serial Old 收集器组合进行内存回收

UseParNewGC

打开此开关后,使用 ParNew + Serial Old 收集器组合进行内存回收(64位服务器被启用警告,未来版本可能删除)

UseConcMarkSweepGC

打开此开关后,使用 ParNew + CMS + SerialOld 的收集器组合进行内存回收。Serial Old 收集器将作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用

UseParallelGC

虚拟机运行在Server 模式下的默认值,打开此开关后,使用 Parallel Scavenge + Serial Old (PS MarkSweep) 的收集器组合进行垃圾手机

UseParallelOldGC

打开此开关后,使用 Parallel Scavenge + Parallel Old 的收集器组合进行垃圾回收

SurvivorRatio

新生代中的 Eden 区域与Survivor 区域容量比值,默认为8,代表 Eden:Survivor = 8:1

PretenureSizeThreshold

直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配

MaxTenuringThreshold

晋升到老年代的对象年龄,每个对象在坚持一次 Minor GC (年轻代GC)后,年轻就增加 1,当超过这个值则进入老年代

HandlePromotionFailure

是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden 和 Survivor 区所有对象都存活的极端情况

ParallelGCThreads

设置并行GC时进行内存回收的线程数

GCTimeRatio

GC时间占总时间的比率,默认值99,即允许1%的GC时间,仅在使用PS收集器时生效

MaxGCPauseMillis

设置GC最大停顿时间,仅在使用PS收集器生效

CMSInitiatingOccupancyFraction

设置CMS收集器在老年代空间被使用多少后触发垃圾收集,默认值为68%,仅在使用CMS收集器时生效

UseCMSCompactAtFullCollection

设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理,仅在使用CMS收集器时生效

CMSFullGCsBeforeCompaction

设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效