【JVM】之 动手模拟 FullGC

文章目录

    • 一、环境
    • 二、分析
      • (1)代码片段1
      • (2)代码片段2
      • (3)代码片段3

一、环境


public class Demo {

    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];
    }
}

参数:

-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 日志如下:

OpenJDK 64-Bit Server VM (25.162-b12) for linux-amd64 JRE (1.8.0_162-8u162-b12-1-b12), built on Mar 15 2018 17:19:50 by "buildd" with gcc 7.3.0
Memory: 4k page, physical 16306976k(1600884k free), swap 2097148k(2033056k free)
CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC 
0.106: [GC (Allocation Failure) 0.106: [ParNew (promotion failed): 7821K->8371K(9216K), 0.0037815 secs]0.110: [CMS: 8194K->6670K(10240K), 0.0034083 secs] 11917K->6670K(19456K), [Metaspace: 3190K->3190K(1056768K)], 0.0073212 secs] [Times: user=0.02 sys=0.00, real=0.00 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 6670K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
 Metaspace       used 3197K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 343K, capacity 388K, committed 512K, reserved 1048576K


二、分析



(1)代码片段1

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

直接分配 4MB 的大对象,此时这个对象会直接进入老年代,array1不再引用这个数组。

如图:
【JVM】之 动手模拟 FullGC_第1张图片



(2)代码片段2

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区域。

如图:
【JVM】之 动手模拟 FullGC_第2张图片



(3)代码片段3

byte[] array6 = new byte[2 * 1024 * 1024];

在(2)的基础上,再执行此行代码。

Eden区,已经放不下 2MB 的对象了,因此会直接触发一次 Young GC

GC日志如下:

0.106: [GC (Allocation Failure) 0.106: [ParNew (promotion failed): 7821K->8371K(9216K), 0.0037815 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 

日志解释:
Eden区已经有 7821KB 的对象,但是回收之后发现一个都回收不了,因为均引用了。

这时候,会把这些对象(3个2MB、1个128KB)直接放入老年代,但这样就超过老年代的10MB的大小了,所以会有如下 gc日志:

0.110: [CMS: 8194K->6670K(10240K), 0.0034083 secs] 11917K->6670K(19456K), [Metaspace: 3190K->3190K(1056768K)], 0.0073212 secs]

此时执行了 CMS垃圾回收器的 FULL GC

full gc:

  1. 对老年代进行 old gc
  2. young gc进行关联
  3. 触发一次元数据区(永久代)的GC -》 metaspace
  1. 触发young gc,把2个2MB的数组放入老年代

如图:

【JVM】之 动手模拟 FullGC_第3张图片

  1. 此时继续放 1个2MB的数组和1个128KB的数组到老年代,一定会放不下,就触发CMSFULL GC

回收掉老年代的 4MB的数组,因为他已经没人引用了。

如图:
【JVM】之 动手模拟 FullGC_第4张图片

老年代中放入 1个 2MB 和 1个128KB的数组,如图:

【JVM】之 动手模拟 FullGC_第5张图片

  1. CMS FULL GC 执行完毕后,新生代的对象都进入老年代。

此时在 新生代分配 2MB的数组就成功了

如图:

【JVM】之 动手模拟 FullGC_第6张图片

你可能感兴趣的:(【JVM】)