对象的内存分布
在 Hotspot VM 中,对象在内存中的存储布局分为 3 块区域:
- 对象头(Header)
- 实例数据(Instance Data)
- 对齐填充(Padding)
对象头又包括三部分:MarkWord、元数据指针、数组长度。
- MarkWord:用于存储对象运行时的数据,好比 HashCode、锁状态标志、GC分代年龄等。这部分在 64 位操作系统下占 8 字节,32 位操作系统下占 4 字节。
- 指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪一个类的实例。
这部分就涉及到指针压缩的概念,在开启指针压缩的状况下占 4 字节,未开启状况下占 8 字节。 - 数组长度:这部分只有是数组对象才有,若是是非数组对象就没这部分。这部分占 4 字节。
实例数据就不用说了,用于存储对象中的各类类型的字段信息(包括从父类继承来的)。再来复习一遍8种基本数据类型所占字节数:
boolean 1byte
byte 1 byte
char 2 byte
short 2 byte
int 4 byte
long 8 byte
float 4 byte
double 8 byte
关于对齐填充,Java 对象的大小默认是按照 8 字节对齐,也就是说 Java 对象的大小必须是 8 字节的倍数。若是算到最后不够 8 字节的话,那么就会进行对齐填充。
关于指针压缩
引用类型在 64 位系统上占用 8 个字节,虽然一个并不大,但是耐不住多。
所以为了解决这个问题,JDK 1.6 开始 64 bit JVM 正式支持了 -XX:+UseCompressedOops (需要jdk1.6.0_14) ,这个参数可以压缩指针。
启用 CompressOops 后,会压缩的对象包括:
- 对象的全局静态变量(即类属性);
- 对象头信息:64 位系统下,原生对象头大小为 16 字节,压缩后为 12 字节;
- 对象的引用类型:64 位系统下,引用类型本身大小为 8 字节,压缩后为 4 字节;
- 对象数组类型:64 位平台下,数组类型本身大小为 24 字节,压缩后 16 字节。
当然压缩也不是万能的,针对一些特殊类型的指针 JVM是不会优化的。 比如:
- 指向非 Heap 的对象指针
- 局部变量、传参、返回值、NULL指针。
JVM在1.6之后,在64位操作系统下都是默认开启指针压缩的。
查看对象在内存中的分布和占用情况
maven项目下导入 jol-core:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.14</version> </dependency>
测试代码:
public class ObjectCore { public static void main(String[] args) { Obj obj = new Obj(); ClassLayout classLayout1 = ClassLayout.parseInstance(obj); System.out.println(classLayout1.toPrintable()); Double i = 1000.0; ClassLayout classLayout2 = ClassLayout.parseInstance(i); System.out.println(classLayout2.toPrintable()); } } class Obj { Integer i; }
输出结果:
参考资料: