对象内存布局
对象头
- Mark Word
○ 32位占4B
○ 64位占8B
○ 哈希值
○ GC分代年龄
○ 锁状态标识
○ 偏向锁线程id
○ 偏向时间戳 - 类型指针 Klass pointer
○ 对象所属的类的元信息的实例指针, instanceKlass在方法区的地址
○ 指针压缩优化
■ 开启 : 4B
■ 不开启 : 8B - 数组长度
○ 如果这个对象不是数组,占0B
○ 如果这个对象是数组,占4B
实例数据
- 类的非静态属性,生成对象时就是实例数据
- 相同长度的数据会放到一起,如long/duble
对齐填充
- Java中所有的对象大小都是8字节对齐 8的整数倍
- 如果一个对象占30B + JVM底层会补2B(对齐填充),凑成32字节,达到8字节对齐
- 对齐填充补0 程序更好写 性能更高
- 数组对象在关闭指针压缩的情况下会出现两端填充
指针压缩
- jdk6以后引入的技术,默认是开启的,参数:-XX:+/-UseCompressedOops
- 开启指针压缩可节省空间/寻址更高效
指针压缩的实现原理
- 由于存在对齐填充,8字节对齐,二进制末尾都是 000
○ 8–> 1 000
○ 16–> 10 000
○ 24–> 11 000
○ 32–> 100 000 - 指针压缩原理就是基于末尾三位000进行压缩
- 存储的时候后三位000抹掉
- 使用的时候后三位000补上,从而实现指针压缩
开启指针压缩的情况下,一个oop表示的最大对空间是多少
- 类型指针占4字节,也就是32位,一个oop,存储的时候是4B,32bit
- 使用的时候,尾部补了三个0, 35bit
- 即2的35次方=32G
32G不够用如何扩容
- 修改OpenJdk源码,调整8字节对齐(压缩抹掉后三位000,32G),调整为16位对齐(压缩抹掉后4位0000,64G)
- 目前CPU算力可能跟不上
计算对象大小
- 用jol-core包或者HSDB都可以看,区别是HSDB只能看基于普通类生成对象的大小,java中的数组因为是运行时生成的,故它的大小只有运行时才能知晓。
- 计算大小 : Mark Word + 类型指针 + 数组长度 + 实例数据 + 对齐填充
空对象(没实例数据)
- 开启指针压缩 : 16B = 8B + 4B + 0B + 0B + 4B
- 关闭指针压缩 : 16B = 8B + 8B + 0 + 0 + 0
public class CountEmptyObjectSize {
public static void main(String[] args) {
CountEmptyObjectSize obj = new CountEmptyObjectSize();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}
普通对象
- 开启指针压缩 : 24 = 8B + 4B + 0B + 4 * 2 + 4B
- 关闭指针压缩 : 24 = 8 + 8 + 0 + 4*2 + 0
public class CountObjectSize {
int a = 10;
int b = 20;
public static void main(String[] args) {
CountObjectSize object = new CountObjectSize();
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}
数组对象
- 开启指针压缩 : 32B = 8 + 4 + 4 + 4 * 3 + 4B
- 关闭指针压缩 : 40B = 8 + 8 + 4 + 4 + 4 * 3 + 4B (两段填充)
public class CountSimpleObjectSize {
static int[] arr = {0, 1, 2};
public static void main(String[] args) {
CountSimpleObjectSize test1 = new CountSimpleObjectSize();
System.out.printf(ClassLayout.parseInstance(arr).toPrintable());
}
}