从头开始学JVM--------理解GC日志
一、需要掌握的虚拟机参数
堆内存命令
-Xms 设置堆最小内存,默认机器内存的 1/64
-Xmx 设置堆最大内存,默认机器内存的 1/4
栈内存命令
-Xss 设置每个线程的栈内存
GC
-XX:+HeapDumpOnOutOfMemoryError 分析堆内存溢出原因
-XX:+HeapDumpPath=/user/local/ 将堆内存溢出原因记录到指定的文件
-XX:+PrintGCDetails 打印GC回收细节
指明垃圾回收器
-XX:+UseG1GC 使用G1垃圾回收器
二、模拟内存溢出
import java.util.ArrayList;
import java.util.List;
public class TestBean {
static class OOMObject {
private byte[] data = new byte[512 * 1024];
}
/**
* VM Args : -Xms5m -Xmx5m -XX:+PrintGCDetails
*/
public static void main(String[] args) {
int count = 0;
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject());
}
}
}
程序运行结果:
三、分析结果
从结果来看,总共发生了4次垃圾回收。
(1)第一次垃圾回收结果日志,仅发生在年轻代中。我手动换行一下,看的更加清楚。
[GC (Allocation Failure)
[PSYoungGen: 1024K->488K(1536K)] 1024K->640K(5632K), 0.0020076 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
GC(Allocation Failure):引起垃圾回收的原因。本次 GC 是因为年轻代中没有足够的空间存放新创建的对象。
PSYoungGen: 1024K->488K(1536K) :年轻代中垃圾回收之前使用了1024K的内存空间,垃圾回收后使用了488K内存空间。1536K表示年轻代的总的内存空间。
1024K->640K(5632K):当前堆内存在垃圾会收前使用了1024K,垃圾回收后使用了640K,总的堆内存大小为5632K。
0.0020076 secs:GC回收时间。单位是秒。本次是2ms。
Times: user=0.00 sys=0.00, real=0.00 secs::GC事件的持续时间
(2)第三次垃圾回收结果日志。发生在 年轻代,老年代,元空间中。
[Full GC (Ergonomics)
[PSYoungGen: 1524K->487K(1536K)]
[ParOldGen: 4087K->4011K(4096K)] 5611K->4498K(5632K),
[Metaspace: 3206K->3206K(1056768K)], 0.0116127 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
Full GC (Ergonomics) :我个人理解是全面的垃圾回收,在整个堆内存中进行回收,而不是仅仅在某个分代中。
PSYoungGen: 1524K->487K(1536K):年轻代垃圾回收前使用了1524K,回收后使用487K,年轻代内存大小共1536K。
ParOldGen: 4087K->4011K(4096K):老年代垃圾回收钱使用了4087K,垃圾回收后使用了4011K,老年代内存总大小为4096K。
5611K->4498K(5632K):堆内存回收前使用了5611K,回收后使用了4498K,堆内存总大小为5632K。
Metaspace: 3206K->3206K(1056768K):元空间垃回收前后都是3206K,说明没有发生垃圾回收。
0.0116127 secs :本次垃圾回收共11ms。
Times: user=0.03 sys=0.00, real=0.01 secs:本次垃圾回收实际花费时间是 0.01秒。
(3)第四次垃圾回收结果日志。发生在 年轻代,老年代,元空间中。
[Full GC (Ergonomics)
[PSYoungGen: 999K->999K(1536K)]
[ParOldGen: 4011K->4010K(4096K)] 5011K->5010K(5632K),
[Metaspace: 3206K->3206K(1056768K)], 0.0097889 secs]
[Times: user=0.00 sys=0.00, real=0.01 secs]
年轻代已经回收不动,老年代仅仅回收了1K。 堆内存从5011K实际减小到5010K,仅仅减少1K。而堆内存最大限制为5632K,因此可以预见到,即将出现内存溢出。果不其然,从程序实际执行结果来看,出现了 OOM。
通过GC日志,可以看出,当前应用程序的内存使用情况十分糟糕,老年代几乎被占满。
所以说,GC日志对监控JVM是否正常运行,有着十分重要的作用。
一旦上线的项目出现了OOM,GC可以快速定位到哪个内存区域出现,真乃救命稻草啊。
四、其它注意点
GC时间的时间不能过长,否则就会影响系统的吞吐量。
老年代内存使用率接近100%,并且回收后仍然是这样,得注意。要么是堆内存太小,要么是系统中出现了内存泄露。