奇怪的GC (Allocation Failure)日志

今天在玩JVM的GC日志时,遇到了一个奇怪的现象。目前的知识还没找到原因,先记录下。找到原因有再补充~。

环境是:JDK8

1. 先上构造场景的代码。

/**
 * @Auther: jx
 * @Date: 2018/7/28 16:37
 * @Description: GC测试2
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
 */
public class TestGC2 {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        test();
    }

    private static void test() {
        byte[] a1, a2, a3, a4;
        a1= new byte[2*_1MB];
        a2= new byte[2*_1MB];
        a3= new byte[2*_1MB];
        a4= new byte[4*_1MB];
    }
}

代码比较简单,创建4个数组对象空间。

2. JVM 配置

2.1配置内容
奇怪的GC (Allocation Failure)日志_第1张图片
VM配置信息
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
2.2 配置说明
配置 说明
-verbose:gc 显示GC的情况
-Xms20M 设置堆初始大小20M
-Xmx20M 设置堆最大大小20M
-Xmn10M 设置堆中 new Generation 新生代的大小为10M
-XX:+PrintGCDetails 输出GC的详细日志
-XX:ServivorRatio=8 设置 New Generation新生代中的 Eden区与Servivor区比例是8:1
-XX:+UseSerialGC 设置GC回收器模式是串型垃圾回收器
2.3 配置结果

这里需要对配置后,进行说明Java堆中各个区域的空间情况。如下:

  • Java堆,共有 20M
  • 新生代,有10M
    • Eden区,8M
    • Servivor From ,1M
    • Servior To ,1M
  • 老年代,有10M

堆内存模型如下:


奇怪的GC (Allocation Failure)日志_第2张图片
内存模型

3. 推导内存分配

  • a1,a2, a3 分配时
    因为分配a1,a2,a3大小为2M,Eden区的大小够a1,a2,a3申请。申请后的内存情况如下:

    奇怪的GC (Allocation Failure)日志_第3张图片
    a1,a2,a3申请空间后的情况

    黄色代表已使用,空着代表未使用。

    • a4 申请分配时
      这里详细步骤如下。
      • ① 因为a4需要申请4M空间,但现在Eden区只有2M Free的空间
      • ② From区还有1M空间,也无法满足 a4的申请。
      • ③ 故此需要执行一次MinorGC才能够给 Allocation分配 a4的4M空间.
      • ④ MinorGC 开始执行,首先会开始处理a1,a2,a3的空间。
      • ⑤ 因为a1,a2,a3有2M的空间,同时大于From区 1M的空间。
      • ⑥ 故此,会将a1,a2,a3移到Tenured老年代区域。
        执行后的内存空间模型如下:


        奇怪的GC (Allocation Failure)日志_第4张图片
        Minor GC执行后

4. 推导GC日志

通过上面的内存模型分析,推测GC日志可能如下:

[GC (Allocation Failure) [DefNew: 6144K->0K(9216K), 0.0040708 secs] 0K->0K(19456K), 0.0041138 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
 def new generation   total 9216K, used 4456K [0, 4, 9)
  eden space 8192K,  50% used [0, 4, 8)
  from space 1024K,   0% used [0, 1, 1)
  to   space 1024K,   0% used [0, 0, 0)
 tenured generation   total 10240K, used 6144K [0, 6, 10)
   the space 10240K,  60% used [0, 6, 6, 10)
 Metaspace       used 224K, capacity 2280K, committed 2368K, reserved 4480K

解释说明:

  • 1.GC信息
    [GC (Allocation Failure) [DefNew: 6144K->0K(9216K), 0.0040708 secs] 0K->0K(19456K), 0.0041138 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    一次分配失败GC,处理新生代从6144K到0 k空间(新生代共9216K空间),堆 共19456K空间)
    1. GC结束后 + a4申请完后的,堆信息-新生代。
      def new generation total 9216K, used 4456K [0, 4, 9)
      新生代共9216k,使用了4456K。(4456K是a4使用的)
      1. 新生代中的eden区、from区、to区的堆信息
eden space 8192K,  50% used [0, 4, 8)
# eden区空间共8192K,使用50%。
  from space 1024K,   0% used [0, 1, 1)
# from区空间共1024K,使用0%
  to   space 1024K,   0% used [0, 0, 0)
# to 区空间共1024K,使用0%
    1. 元空间信息
 Metaspace       used 224K, capacity 2280K, committed 2368K, reserved 4480K

5. 实际GC日志

实际情况却让我有点不明白,先看结果。

[GC (Allocation Failure) [DefNew: 7177K->1024K(9216K), 0.0040750 secs] 7177K->5207K(19456K), 0.0041222 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 7408K [0x05200000, 0x05c00000, 0x05c00000)
  eden space 8192K,  77% used [0x05200000, 0x0583c210, 0x05a00000)
  from space 1024K, 100% used [0x05b00000, 0x05c00000, 0x05c00000)
  to   space 1024K,   0% used [0x05a00000, 0x05a00000, 0x05b00000)
 tenured generation   total 10240K, used 4183K [0x05c00000, 0x06600000, 0x06600000)
   the space 10240K,  40% used [0x05c00000, 0x06015ec0, 0x06016000, 0x06600000)
 Metaspace       used 224K, capacity 2280K, committed 2368K, reserved 4480K
  • 首先,new generation 共9M,使用了7M.
    • eden,共8M,用了77%,大概是6M.
    • from,共1M,用了100%,是1M.
  • Tenuerd Generation 共10M,使用了4M.

看到这个结果,有点懵...有以下几个疑惑:

  • 问题1. a1,a2,a3,a4总共申请的空间是2+2+2+4 =10M。
    而为什么使用 New Generation + Tenuerd Generation = 11M ?哪里多出来了1M?(见鬼了...)
  • 问题2. New Generation 为什么是使用了7M?
    其中,eden区为什么是6M?
    From区又为什么会被使用?而且是使用了全部的1M.
  • 问题3. Tenuerd Generation 为什么只有4M?
初步整理
    1. 空间分配担保?
      空间使用多出来了1M,分析可能是因为【空间分配担保】。可是这块好像挺复杂的,等再详细了解后,进一步写。
    1. Java8?
      原先还在想,因为《深入理解Java虚拟机》的环境是Java7 。是否因环境Java8 导致?但实际测试Java7 一样有此问题。排除了Java8的环境因素。
    1. 不知名隐藏规则?
      应该是还有一些JVM的规则机制没有搞明白。

End...写在最后

    1. 看书很好,但要动手,总能发现不一样的东西。
    1. 问题多了,知识也就多了。
    1. 还需要进一步深入理解...
      《深入理解Java虚拟机》看一遍神书是不够的!!!
  • 最后,等我找答案...

你可能感兴趣的:(奇怪的GC (Allocation Failure)日志)