热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM:33如何查看JVM的FullGC日志

1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr

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。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文总结了Java程序设计第一周的学习内容,涵盖语言基础、编译解释过程及基本数据类型等核心知识点。 ... [详细]
  • HBase运维工具全解析
    本文深入探讨了HBase常用的运维工具,详细介绍了每种工具的功能、使用场景及操作示例。对于HBase的开发人员和运维工程师来说,这些工具是日常管理和故障排查的重要手段。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • andr ... [详细]
  • 本文提供了使用Java实现Bellman-Ford算法解决POJ 3259问题的代码示例,详细解释了如何通过该算法检测负权环来判断时间旅行的可能性。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • Java编程实践:深入理解方法重载
    本文介绍了Java中方法重载的概念及其应用。通过多个示例,详细讲解了如何在同一类中定义具有相同名称但不同参数列表的方法,以实现更灵活的功能调用。 ... [详细]
author-avatar
品花人生1
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有