1 JDK8打印并分析GC日志
1.1 打印日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+UseSerialGC -Xmx1m -Xloggc:./gc-serial.log
参数 功能
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:gc.log 日志文件的输出路径
使用Serial收集器打印日志
# JDK相关信息
Java HotSpot(TM) 64-Bit Server VM (25.221-b11) for windows-amd64 JRE (1.8.0_221-b11), built on Jul 4 2019 04:39:29 by "java_re" with MS VC++ 10.0 (VS2010)
# 内存相关信息
Memory: 4k page, physical 16647476k(6953016k free), swap 25560372k(12208608k free)
# 当前使用的JVM参数
CommandLine flags: -XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:InitialHeapSize=1048576 -XX:+ManagementServer -XX:MaxHeapSize=1048576 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
# Young GC
2022-01-04T21:59:21.976+0800: 0.058: [GC (Allocation Failure) 2022-01-04T21:59:21.976+0800: 0.058: [DefNew: 512K->63K(576K), 0.0008255 secs] 512K->437K(1984K), 0.0009437 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[当前时间戳:相对时间戳]:[[造成GC的原因]:[时间戳]:[年轻代: 回收前->回收后(年轻代总大小), 耗时] 回收前堆大小->回收后堆大小(堆总大小), 耗时] [用户耗时, 系统耗时,实际耗时]
# Full GC
2022-01-04T21:59:22.466+0800: 0.546: [Full GC (Allocation Failure) 2022-01-04T21:59:22.466+0800: 0.546: [Tenured: 1407K->1407K(1408K), 0.0032359 secs] 1983K->1519K(1984K), [Metaspace: 7463K->7463K(1056768K)], 0.0033010 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
格式分析类似于Young GC,打印老年代、元空间使用情况。
# 堆内存使用情况(年轻代、老年代、元空间)
Heap
def new generation total 576K, used 566K [0x00000000ffe00000, 0x00000000ffea0000, 0x00000000ffea0000)
eden space 512K, 100% used [0x00000000ffe00000, 0x00000000ffe80000, 0x00000000ffe80000)
from space 64K, 84% used [0x00000000ffe80000, 0x00000000ffe8d878, 0x00000000ffe90000)
to space 64K, 0% used [0x00000000ffe90000, 0x00000000ffe90000, 0x00000000ffea0000)
tenured generation total 1408K, used 1408K [0x00000000ffea0000, 0x0000000100000000, 0x0000000100000000)
the space 1408K, 100% used [0x00000000ffea0000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 9091K, capacity 9336K, committed 9472K, reserved 1056768K
class space used 1083K, capacity 1154K, committed 1280K, reserved 1048576K
- 对于不同内存区域,不同垃圾收集器展示名称不同。例如年轻代:DefNew(Serial收集器)、ParNew(ParNew收集器)、PSYoungGen(Parallel Scavenge收集器)。
CMS收集器
CMS触发垃圾回收时,打印各个GC环节。
# 初始标记
1.082: [GC (CMS Initial Mark) [1 CMS-initial-mark: 5895K(6848K)] 6267K(9920K), 0.0005152 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
# 并发标记
1.083: [CMS-concurrent-mark-start]
1.086: [CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.06 sys=0.00, real=0.00 secs]
# 并发预清理
1.086: [CMS-concurrent-preclean-start]
1.087: [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
# 重新标记
1.087: [GC (CMS Final Remark) [YG occupancy: 693 K (3072 K)]1.087: [Rescan (parallel) , 0.0006206 secs]1.088: [weak refs processing, 0.0000278 secs]1.088: [class unloading, 0.0012325 secs]1.089: [scrub symbol table, 0.0015527 secs]1.091: [scrub string table, 0.0002195 secs][1 CMS-remark: 5895K(6848K)] 6589K(9920K), 0.0037899 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
# 并发清理
1.091: [CMS-concurrent-sweep-start]
1.092: [CMS-concurrent-sweep: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
# 重置
1.093: [CMS-concurrent-reset-start]
1.093: [CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
G1收集器
# 这是一个年轻代GC,花费了0.0035900。下面的缩进,表示这行日志的子任务
2020-05-08T14:19:58.598-0800: 5.599: [GC pause (G1 Evacuation Pause) (young), 0.0035900 secs]
# 并行任务,并行GC花费2.4毫秒,并行阶段有8个线程
[Parallel Time: 2.4 ms, GC Workers: 8]
# 表示各个GC工作线程在应用启动多久(毫秒)后启动。
# 同时还做了个统计,例如这些GC线程最早启动的那个线程在应用启动后847.9毫秒后启动等
[GC Worker Start (ms): Min: 5598.7, Avg: 5599.1, Max: 5600.8, Diff: 2.1]
# 表示各个GC工作线程扫描跟对象花费的时间的统计
[Ext Root Scanning (ms): Min: 0.0, Avg: 0.3, Max: 0.7, Diff: 0.7, Sum: 2.4]
# 表示各个GC工作线程更新Remembered Sets花费的时间的统计
# Remembered Sets是保存到堆中的区域的跟踪引用。设值方法线程持续改变对象图,自此引指向一个特定的区域。我们保存这些改变的跟踪信息到叫作Update Buffers的更新缓存中。Update RS子任务不能并发的处理更新缓存,更新一致所有区域的Remembered Sets
[Update RS (ms): Min: 0.0, Avg: 0.8, Max: 1.8, Diff: 1.8, Sum: 6.4]
# 表示每个GC工作线程处理的Update Buffers的数量统计
[Processed Buffers: Min: 0, Avg: 2.2, Max: 6, Diff: 6, Sum: 18]
# 每个GC工作线程扫描Remembered Sets花费的时间
# 一个区域的Remembered Sets包含指向这个区域的引用的相符合的卡片。这个阶段扫描这些卡片寻找指向所有这些区域的Collection Set的引用
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
# 扫描Code Root耗时统计。Code Root是JIT编译后的代码里引用了heap中的对象
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
# 拷贝存活对象到新的Region耗时统计
[Object Copy (ms): Min: 0.0, Avg: 0.2, Max: 0.4, Diff: 0.4, Sum: 1.7]
# 各个GC工作线程完成任务后尝试中断GC线程到真正中断的耗时统计
# 在某个GC线程中断之前,会检查其它线程的工作队列,如果发现依然有任务,会帮助处理,之后再中断
[Termination (ms): Min: 0.0, Avg: 0.5, Max: 0.7, Diff: 0.7, Sum: 3.7]
# 尝试中断次数统计
[Termination Attempts: Min: 1, Avg: 2.4, Max: 12, Diff: 11, Sum: 19]
# GC工作线程花费在其他工作上的时间统计
[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.9, Diff: 0.9, Sum: 0.9]
# 各个GC工作线程花费的时间总和统计
[GC Worker Total (ms): Min: 0.2, Avg: 1.9, Max: 2.3, Diff: 2.2, Sum: 15.3]
# 各个GC工作线程线程的结束时间,min|max分别表示第一个|最后一个线程的结束时间。
[GC Worker End (ms): Min: 5601.0, Avg: 5601.0, Max: 5601.0, Diff: 0.0]
# 串行任务,修复GC期间code root指针改变的耗时
[Code Root Fixup: 0.0 ms]
# 串行任务,清除Code Root耗时
[Code Root Purge: 0.0 ms]
# 清除Card Table中的Dirty Card的耗时
[Clear CT: 0.1 ms]
# 其他任务
[Other: 1.1 ms]
# 为Collection Set选择区域所花费的时间
[Choose CSet: 0.0 ms]
# 花费在处理引用对象上的时间
[Ref Proc: 0.7 ms]
# 引用入队到ReferenceQueues花费的时间,可用-XX:+ParallelRefProcEnabled,并行处理这一步
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.3 ms]
# 处理超大对象
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
# 释放Collection Set数据结构花费的时间
[Free CSet: 0.0 ms]
# 各个区域的内存变化。
# 4096.0K:伊甸园当前占用4096.0K
# (4096.0K):伊甸园总大小4096.0K
# 0.0B:收集后,伊甸园占用将会变成0
# (4096.0K):伊甸园的目标大小(如有需要,JVM可能会自动增加伊甸园大小)
[Eden: 4096.0K(4096.0K)->0.0B(4096.0K) Survivors: 1024.0K->1024.0K Heap: 24.3M(30.0M)->20.6M(30.0M)]
# 用户耗时、系统耗时、实际耗时
[Times: user=0.01 sys=0.00, real=0.01 secs]
# 开始扫描初始标记阶段Survivor区的Root Region
2020-05-08T14:19:54.087-0800: 1.087: [GC concurrent-root-region-scan-start]
# 扫描完成
2020-05-08T14:19:54.091-0800: 1.092: [GC concurrent-root-region-scan-end, 0.0049225 secs]
# 2. 并发标记,标记线程数可用-XX:ConcGCThreads指定
2020-05-08T14:19:54.092-0800: 1.092: [GC concurrent-mark-start]
# 并发标记结束
2020-05-08T14:19:54.102-0800: 1.103: [GC concurrent-mark-end, 0.0106528 secs]
# 3. 最终标记(stop the world)
2020-05-08T14:19:54.103-0800: 1.104: [GC remark 2020-05-08T14:19:54.103-0800: 1.104: [Finalize Marking, 0.0028699 secs] 2020-05-08T14:19:54.106-0800: 1.107: [GC ref-proc, 0.0001689 secs] 2020-05-08T14:19:54.106-0800: 1.107: [Unloading, 0.0053988 secs], 0.0087250 secs]
[Times: user=0.01 sys=0.00, real=0.01 secs]
# 4. 筛选回收(stop the world)
# 没有存活对象的Old Region和Humongous Region将被释放和清空。
# 为了准备下次GC,在CSets中的Old Regions会根据他们的回收收益的大小排序。
2020-05-08T14:19:54.114-0800: 1.114: [GC cleanup 15M->14M(30M), 0.0006027 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
# 并发清理开始
2020-05-08T14:19:54.114-0800: 1.115: [GC concurrent-cleanup-start]
# 并发清理结束
2020-05-08T14:19:54.114-0800: 1.115: [GC concurrent-cleanup-end, 0.0000133 secs]
JDK11打印日志
JDK11版本使用XLog统一管理日志,只需要一个日志参数。
-XX:+PrintGCDetails -XX:+UseG1GC -Xmx10m -Xloggc:./gc-serial.log