前言
呵呵 最近在看这样一篇文章的时候, eden区没有发生minor gc,对象直接分配在了old gen
看到了 R大 的叱咤风云, 讲解的非常细致, 十分令人佩服, 然后 若是想有所收获, 还得 构造一下这个情况, 复现一下, 然后 调试着走一次, 才能 有所收获, 嘿嘿
当然 由于 vm 版本不一样, 因此 下面的测试用例的相关 选项 我这里做了一些 调整 一下代码, 截图 基于 jdk9
测试用例如下
package com.hx.test04;
/**
* AllocationTest
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2020-03-06 15:24
*/
public class Test03AllocationTest {
/**
* constants
*/
private static final int _1KB = 1024;
private static final int _1MB = _1KB * 1024;
// Test03AllocationTest
// refer : https://hllvm-group.iteye.com/group/topic/38293
/**
* -Xint -Xmx100M -XX:+UseParallelGC -XX:-UseTLAB -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:NewSize=28M
*/
public static void main(String[] args) {
testAllocation();
}
// testAllocation
public static void testAllocation() {
byte[] byte1 = new byte[_1MB*5];
byte[] byte2 = new byte[_1MB*10];
byte1 = null;
byte2 = null;
byte[] byte3 = new byte[_1MB*5];
byte[] byte4 = new byte[_1MB*10];
byte3 = null;
byte4 = null;
byte[] byte5 = new byte[_1MB*15];
}
}
跑的时候 需要加上 如上的相关 vm 参数, 但是 普通的 跑这个 测试用例 看不到太多的东西, 需要结合 具体的调试, 才能 了解到 R大 所说的一系列描述的意思
第一个字节数组的数据分配
可以看到, 这里是 第一个 "new byte[_1MB*5]", length 为 5M, size 怎么看起来和 length 没什么关系啊 ?
呵呵 我最开始看到这里也奇怪, 直到看了一下 "size_t size = typeArrayOopDesc::object_size(layout_helper(), length);" 的代码
原来这里的 size = (length + header_size) / WordPerBytes 计算出来的, BytesPerWord 是指一个字对应多少个字节, 64位操作系统中其值为 8
呵呵 我最开始看觉得有些奇怪是因为 我发现这个 size 怎么比 length 大这么多??, 不就装字节数据 + mark + klass + length 么, 咋个会多出这么多, 后来仔细一看原来 size 是比 length 少了一位。
第一次字节数组的分配细节
分配之前 young_gen 的 used 为 4.18M, capacity 为 24.50M(eden:21M, from:3.5M)
eden 的 used 为 4.18M, capacity 为 21M
我们这里分配的是 5M, 可以分配, 就直接从 young_gen 里面分配了
创建第一个字节数组之后 young_gen used 为 9.18M
第二次数组分配的细节
分配之前 young_gen 的 used 为 9.18M, capacity 为 24.50M(eden:21M, from:3.5M)
eden 的 used 为 9.18M, capacity 为 21M
堆的相关数据 和 第一次数组分配之后的结果一样, 只是这里需要分配的字节数组 变成了 10M
这里 young_gen 依然能够分配第二个数组
分配之后 young_gen used 为 19.18M
第三次数组分配的细节
分配之前 young_gen 的 used 为 19.18M, capacity 为 24.50M(eden:21M, from:3.5M)
eden 的 used 为 19.18M, capacity 为 21M
到这里 young_gen 分配不了 这个 5M 的字节数组了
后面尝试 在 young_gen 再重新分配, 失败
尝试在 old_gen 分配空间, 不满足条件
gc_locker_stalled_count, GCLocker::is_active_and_needs_gc 也不满足条件
尝试在 old_gen 分配空间的条件如下
这里的 GCLocker::is_active_and_needs_gc 和 _death_march_count 在这里不满足条件
R大 评论中也是这个判断, 只是由于 版本不一样, 细节上存在一些差异
然后触发了一次 minor gc
VM_ParallelGCFailedAllocation 处理了之后 为第三个字节数组 分配了空间
分配之后 eden 的 used 为 5.00M
第四次数组分配细节如下
分配之前 young_gen 的 used 为 5.00M, capacity 为 24.50M(eden:21M, from:3.5M)
eden 的 used 为 5.00M, capacity 为 21M
可以明显看到 eden 是足以分配 这个 10M 的字节数组的, 因此这里 就在 young_gen 分配
分配之后 eden 的 used 为 15.00M
第五次数组分配的细节
分配之前 young_gen 的 used 为 15.00M, capacity 为 24.50M(eden:21M, from:3.5M)
eden 的 used 为 15.00M, capacity 为 21M
在 young_gen 已经分配不下 这里的 15M 的数组
根据上面的 是否需要在 old_gen 分配的条件之一, "size > eden_space.capacity_in_words/2", 来比较的话
我们这里 size 是 1966082, eden_space.capacity_in_words/2 为 1376256, 是符合 在 old_gen 分配的条件的, 因此 这里第五个 字节数组 就在 old_gen 分配了
这上面的 比较还可以换一个方式, 上面的比较的单位是 字, 我们换成字节的话 会更清晰一些, eden 的空间是 21 M, 一半为 10.5M, 然后 这里申请的空间为 15M, 15M > 10.5M, 因此 可以在 old_gen 里面分配