今天在玩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配置内容
-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
堆内存模型如下:
3. 推导内存分配
-
a1,a2, a3 分配时
因为分配a1,a2,a3大小为2M,Eden区的大小够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老年代区域。
执行后的内存空间模型如下:
- a4 申请分配时
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空间) -
- GC结束后 + a4申请完后的,堆信息-新生代。
def new generation total 9216K, used 4456K [0, 4, 9)
新生代共9216k,使用了4456K。(4456K是a4使用的)
- 新生代中的eden区、from区、to区的堆信息
- GC结束后 + a4申请完后的,堆信息-新生代。
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%
- 元空间信息
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?
初步整理
- 空间分配担保?
空间使用多出来了1M,分析可能是因为【空间分配担保】。可是这块好像挺复杂的,等再详细了解后,进一步写。
- 空间分配担保?
- Java8?
原先还在想,因为《深入理解Java虚拟机》的环境是Java7 。是否因环境Java8 导致?但实际测试Java7 一样有此问题。排除了Java8的环境因素。
- Java8?
- 不知名隐藏规则?
应该是还有一些JVM的规则机制没有搞明白。
- 不知名隐藏规则?
End...写在最后
- 看书很好,但要动手,总能发现不一样的东西。
- 问题多了,知识也就多了。
- 还需要进一步深入理解...
《深入理解Java虚拟机》看一遍神书是不够的!!!
- 还需要进一步深入理解...
- 最后,等我找答案...