JVM:33 如何查看JVM的Full GC日志

1. 示例代码

package com.webcode;

public class Demo4 {
	
	public static void main(String[] args){
		byte[] array1 = new byte[4 * 1024 * 1024];
		array1 = null;
		byte[] array2 = new byte[2 * 1024 * 1024];
		byte[] array3 = new byte[2 * 1024 * 1024];
		byte[] array4 = new byte[2 * 1024 * 1024];
		byte[] array5 = new byte[128 * 1024];
		
		byte[] array6 = new byte[2 * 1024 * 1024];
	}
}

2. GC日志

采用如下JVM参数来运行上述程序:

-XX:NewSize=10485760 -XX:MaxNewSize=10485760 -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:PretenureSizeThreshold=3145728 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log

这里最关键的一个参数,是 “-XX:PretenureSizeThreshold=3145728” ,该参数设置大对象阈值为3MB,也就是超过3MB,就直接进入老年代。

运行之后的GC日志如下:

0.113: [GC (Allocation Failure) 0.113: [ParNew (promotion failed): 7256K->7953K(9216K), 0.0028080 secs]0.116: [CMS: 8194K->6805K(10240K), 0.0035630 secs] 11352K->6805K(19456K), [Metaspace: 2644K->2644K(1056768K)], 0.0068409 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
 par new generation   total 9216K, used 2130K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  26% used [0x00000000fec00000, 0x00000000fee14930, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
 concurrent mark-sweep generation total 10240K, used 6805K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 2651K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 286K, capacity 386K, committed 512K, reserved 1048576K

3. 日志分析

首先看如下代码:

byte[] array1 = new byte[4 * 1024 * 1024];
        array1 = null;

这行代码直接分配了一个4MB的大对象,此时这个对象会直接进入老年代,接着array1不再引用这个对象。

如下图所示:

接着看如下代码:

        byte[] array2 = new byte[2 * 1024 * 1024];
        byte[] array3 = new byte[2 * 1024 * 1024];
        byte[] array4 = new byte[2 * 1024 * 1024];
        byte[] array5 = new byte[128 * 1024];

 连续分配了4个数组,其中3个是2MB的数组,1个是128KB的数组,如下图所示,全部会进入Eden区域中。

接着会执行如下代码:byte[] array6 = new byte[2 * 1024 * 1024];。

因为Eden 区已经放不下了,所以此时会触发一次Young GC。对应的GC日志如下:

ParNew (promotion failed): 7256K->7953K(9216K), 0.0028080 secs

 这行日志显示,Eden区原来有700多KB的对象,但是回收之后发现一个都回收不掉,因为都被变量引用了。

所以,正常来说一定会直接把这些对象放入到老年代里去,但是此时老年代已经有一个4MB的数组了,不能再放下3个2MB的数组和1个128KB的数组。

 接着有如下GC日志:

[CMS: 8194K->6805K(10240K), 0.0035630 secs] 11352K->6805K(19456K), [Metaspace: 2644K->2644K(1056768K)], 0.0068409 secs]

上述日志,说明执行了CMS垃圾回收器的Full GC。也就是对老年代进行Old GC,同时一般会跟一次Young GC关联,并触发一次元数据区(永久代)的GC。

在CMS Full GC之前,就已经触发过Young GC了,此时可以看到Young GC已经有了,接着就是执行针对老年代的Old GC,如下GC日志:

CMS: 8194K->6805K(10240K), 0.0035630 secs

这里看到老年代从8MB左右的对象占用,变成了6MB多的对象占用。这个过程是,在Young GC之后,先把2个2MB的数组放入了老年代,如下图:

此时要继续放1个2MB的数组和1个128KB的数组到老年代,一定会放不下,所以就会触发CMS的Full GC。从而回收掉其中的一个4MB的数组,因为它没有引用了。

 接着放入一个2MB的数组和1个128KB的数组,如下图所示:

再结合CMS的垃圾回收日志:  CMS: 8194K->6805K(10240K), 0.0035630 secs ,它是从回收前的8MB变成了6MB。

最后在CMS Full GC执行完毕之后,年轻代的对象都进入了老年代,此时最后一行代码要在年轻代分配2MB的数组就可以成功了。

4.深入解析

(1)4MB的大对象Array1给分配内存空间时直接进入老年代,此时不会有GC。因为老年代10M可连续的空闲空间;

(2)当年轻代Eden区域分配完3个2M和1个128K对象后,当分配第四个2M对象时。此时Eden空间不够导致分配失败,触发Minor GC,此时新生代存活的对象6M+128K大于老年代的可用连续空间,但是老年代可用连续空间大于历代新生代进入老年代对象的平均大小0,所以分配担当成功,不需要触发Full GC执行Minor GC;

(3)执行Minor GC年轻代6M+128K对象都存活,但是S0只有1M空间导致存活对象进入老年代;

(4)年轻代先将活着的两个2M对象和128K的对象放入老年代,此时老年代8M+128K,当分配最后一个2M对象触发Full GC,因为可用连续空间小于了2M。此时老年代其中4M大对象可回收2个2M对象和1个128K对象不能回收,Full GC回收完4M对象后再将新生代最后活着的2M对象加入到老年代,所以Minor GC时内存空间是没变的。因为对象都活着,Full GC从8M变成6M。

5.总结

上述案例,当年轻代存活的对象太多放不下老年代了,就会触发CMS的Full  GC。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您可以通过以下步骤查看 JVM Full GC 的具体原因: 1. 首先,您需要在 JVM 启动时添加以下参数:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:<log_file_path> 2. 然后,您需要分析生成的 GC 日志文件,查找 Full GC 的记录。 3. 在 Full GC 记录中,您可以查看以下信息: - Full GC 发生的原因,例如内存不足、永久代满、CMS GC 失败等。 - Full GC 前后堆内存的使用情况,以及 GC 前后各代内存的使用情况。 - Full GC 所花费的时间,以及 Full GC 后堆内存的使用情况。 通过分析这些信息,您可以确定 Full GC 的具体原因,并采取相应的措施来优化应用程序的性能。 ### 回答2: 查看JVM Full GC的具体原因可以通过以下步骤进行: 1. 监控工具:使用一些常见的JVM性能监控工具,如JConsole、VisualVM、Java Mission Control等。这些工具可以提供实时的JVM运行信息,其中包含Full GC的相关指标和堆内存的使用情况。 2. 日志分析:查看应用程序的日志文件,搜索其中包含GC的相关日志。根据GC日志中的时间戳、GC类型和相关指标(如堆内存的使用情况、对象生命周期等),可以进一步分析Full GC的原因。 3. 分析GC日志:当JVMGC日志被启用时,可以通过分析GC日志来了解Full GC的具体原因。GC日志中会记录GC活动的详细信息,包括GC类型、GC时间、GC前后堆内存的使用情况等。 4. 堆内存分析工具:使用一些堆内存分析工具,如Eclipse Memory Analyzer Tool(MAT),通过导入堆转储快照文件,可以分析堆内存中的对象分布、对象引用关系等,从而找出可能导致Full GC的原因。例如,一些内存泄漏或者大对象的创建可能导致堆内存不足,进而引发Full GC的发生。 5. JVM参数调整:根据分析结果,如果是堆内存不足导致Full GC的话,可以考虑调整JVM的相关参数,如-Xmx(最大堆大小)、-Xms(初始堆大小)等,增加堆内存的分配。 综上所述,通过使用监控工具、分析GC日志、堆内存分析工具以及调整JVM参数等方法,可以查看JVM Full GC的具体原因。 ### 回答3: 要查看JVM Full GC的具体原因,可以按照以下步骤进行: 1. 设置JVM日志级别:在启动JVM时,使用-XX:+PrintGCDetails或-XX:+PrintGCTimeStamps等参数,将JVMGC日志级别设置为详细模式。这样可以确保在日志中记录Full GC事件的详细信息。 2. 分析GC日志:定期检查和分析JVMGC日志。在GC日志中,Full GC事件通常以“Full GC”或“Full GC(System)”的形式出现。同时会显示一些关键信息,如Full GC消耗的时间、GC前后堆内存的情况等。 3. 查看GC原因:在GC日志中,找到Full GC事件的触发原因。可能的原因包括年轻代或老年代空间不足、永久代空间不足、老年代对象引用链过长等。根据Full GC事件的触发原因,可以进一步分析和解决问题。 4. 使用工具进行分析:可以使用一些专门的工具来分析GC日志,如GCViewer、GCMV等。这些工具可以图形化地展示GC事件的情况,包括GC发生的次数、GC消耗的时间、堆内存的变化等。通过这些工具,可以更直观地查看和分析Full GC的具体原因。 5. 进行性能调优:根据Full GC的具体原因,进行相应的性能调优操作。例如,如果是堆内存不足导致的Full GC,可以通过增加堆内存大小来解决问题;如果是对象引用链过长导致的Full GC,可以优化代码,减少对象间的引用链长度等。 通过以上方法,可以查看JVM Full GC的具体原因,并根据需要进行相应的优化和调整,以提高系统性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值