目录
GC日志的重要性
JVM调优的关键,无论是对于自己熟悉或者不熟悉的程序而言,做调优之前应该要读懂GC后产生的日志
GC日志相关JVM参数
bash
# 打印GC日志(只输出垃圾收集时堆的总体变化)
-XX:+PrintGC
# 打印GC详细信息
-XX:+PrintGCDetails
# 输出GC的时间戳
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
# 发生GC前后打印出堆的信息
-XX:+PrintHeapAtGC
# 指定GC日志文件的保存路径
-Xloggc:/xxx/xxx/xx.log
通过GC日志分析堆空间总体变化
测试程序
java
package oom;
/**
* -Xms8m -Xmx8m -XX:+PrintGC
*/
public class PrintGCLog {
public static void main(String[] args) {
while (true) {
new ObjectTestPrintGC();
}
}
private static class ObjectTestPrintGC {
private int[] data = new int[128];
}
}
JVM参数设置
bash
-Xms8m -Xmx8m -XX:+PrintGC
GC日志输出
bash
[GC (Allocation Failure) 2216K->1102K(7680K), 0.0005381 secs]
[GC (Allocation Failure) 2574K->1740K(7680K), 0.0005690 secs]
[GC (Allocation Failure) 3276K->1900K(7680K), 0.0006052 secs]
GC日志分析
GC类型 + GC原因 + 堆空间在GC前后的变化 + 堆总空间大小 + GC耗时
1、GC的类型
- GC:YoungGC
- Full GC:全面收集,收集新生代、老年代和元空间中的垃圾
2、GC的原因
- Allocation Failure:对象分配内存失败
3、GC前,堆空间已用大小
- 日志片段中的三次分别为:2216K、2574K、3276K
4、GC后,堆空间已用大小
- 日志片段中的三次分别为:1102K、1740K、1900K
5、堆空间总大小
- 7680K
6、GC所耗时长
- 日志片段中的三次分别为:0.0005381secs、0.0005690secs、0.0006052 secs
查看GC日志详细信息
测试案例
测试代码
java
package oom;
import java.util.ArrayList;
import java.util.List;
public class PrintGCDetailsLog {
public static void main(String[] args) {
List<Object> lists = new ArrayList<>();
while (true) {
lists.add(new PrintGCDetailObject());
}
}
private static class PrintGCDetailObject {
private int[] data = new int[1024];
}
}
JVM参数设置
bash
-Xms8m -Xmx8m -XX:+PrintGCDetails
GC详细日志(经过整理)
bash
[GC (Allocation Failure)
[PSYoungGen: 2047K->512K(2048K)] 4857K->4855K(7680K), 0.0010531 secs]
[Times: user=0.00 sys=0.01, real=0.00 secs]
[Full GC (Ergonomics)
[PSYoungGen: 512K->0K(2048K)]
[ParOldGen: 4343K->3921K(5632K)] 4855K->3921K(7680K),
[Metaspace: 3281K->3281K(1056768K)], 0.0034813 secs]
[Times: user=0.01 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 2048K, used 134K [0x00000007bfd80000, 0x00000007c0000000, 0x00000007c0000000)
eden space 1536K, 8% used [0x00000007bfd80000,0x00000007bfda1a30,0x00000007bff00000)
from space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)
to space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
ParOldGen total 5632K, used 900K [0x00000007bf800000, 0x00000007bfd80000, 0x00000007bfd80000)
object space 5632K, 15% used [0x00000007bf800000,0x00000007bf8e1318,0x00000007bfd80000)
Metaspace used 3380K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 370K, capacity 388K, committed 512K, reserved 1048576K
分析YoungGC日志
- GC:YoungGC
- Allocation Failure:给对象分配内存,但内存不足
- PSYoungGen:使用Parallel Scavenge收集器执行新生代垃圾回收
- 2047K->512K(2048K):GC前后新生代空间已被占用大小(新生代空间总大小)
- 4857K->4855K(7680K):GC前后堆空间已被占用大小(堆空间总大小)
- 0.0010531 secs:本次GC的耗时
分析FullGC日志
- Full GC:全局Full GC
- Ergonomics:触发老年代空间分配担保机制
- PSYoungGen:Parallel Scavenge收集器执行新生代垃圾回收
- 512K->0K(2048K):GC前后新生代已被占用空间大小(新生代空间总大小)
- ParOldGen:Parallel Old收集器执行老年代垃圾回收
- 4343K->3921K(5632K):GC前后老年代已被占用空间大小(老年代空间总大小)
- 4855K->3921K(7680K):GC前后堆空间已被占用空间大小(堆空间总大小)
- Metaspace:回收元空间
- 3281K->3281K(1056768K):GC前后元空间已被占用大小(元空间被分配到的总大小)
- 0.0034813 secs:本次GC的耗时
诱发GC的原因
在JDK1.8源码中,gcCause.cpp文件中定义了运行时触发GC的原因,这里列举出其中几种原因
- Allocation Failure:给对象分配空间时内存不足触发的GC
- Metadata GC Threshold:元空间不足触发的GC
- Last ditch collection:元空间分配数据失败且无法扩容时触发的GC
- Ergonomics:使用Parallel Scavenge + Parallel Old垃圾收集器时,老年代空间分配担保触发的GC
- Tenured Generation Full:老年代空间不足触发的GC
- Heap Inspection Initiated GC:通过jmap命令进行堆检测时触发的GC
- Heap Dump Initiated GC:通过jmap命令进行堆转储时触发的GC
- System.gc():程序中手动调用方法触发GC