Java对象模型
Java对象由 对象头+实例数据+padding填充三部分组成;
对象头
- MarkWord:用于存储对象运行时的数据,好比 HashCode、锁状态标志、GC分代年龄等。这部分在 64 位操作系统下占 8 字节,32 位操作系统下占 4 字节;
- 元数据指针:开启指针压缩时占4byte,未开启时占8byte,对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪一个类的实例。
- 数组长度:只有是数组对象才有,若是是非数组对象就没这部分。这部分占 4 字节。
数据实例就是存储对象内所有字段的数据,包括从父类继承的字段;
padding是对象的对齐填充部分,Java对象的大小必须是8字节的倍数,若对象的大小到最后不够8个字节就要补齐。
所以总结下来64位平台下对象大小的总体公式就是:
对象头+实例数据+padding填充=(8byte+8byte=16byte)+ 数据实例 + padding填充;
若开启指针压缩则对象头指针占内存缩小到4个字节,则对象头大小为12个字节;
若对象是类型是数组,则对象头要再加上4个字节,未开启压缩时为20个字节,开启后为16个字节;
开启对象指针压缩
vm默认开启普通对象指针压缩(命令添加参数 -XX:+UseCompressedOops),但当最大堆大小超过32 GB时,JVM将自动关闭 oops 压缩。
注意:普通对象指针压缩(UseCompressedOops)默认就是开启的;
当 Java 堆大小大于 32GB 时也可以使用压缩指针。虽然默认对象对齐是 8 个字节,但可以使用 -XX:ObjectAlignmentInBytes 配置字节值。指定的值应为 2 的幂,并且必须在 8 和 256 的范围内。
除了对象指针压缩,还有类指针压缩:
-XX:+UseCompressedClassPointers:表示是否启用类指针压缩,因为对于任何一个jvm中的对象而言,其内部都有一个指向自己对应类(属于哪个class)的指针(Java习惯叫引用),在64位的Java虚拟机中,默认是启动压缩的;
基本类型对象占用内存空间
类型 | 占用空间(byte) |
boolean | 1 |
byte | 1 |
short | 2 |
char | 2 |
int | 4 |
float | 4 |
long | 8 |
double | 8 |
引用类型占用内存空间:引用类型在 32 位系统上每个引用对象占用 4 byte,在 64 位系统上每个引用对象占用 8 byte。所以每声明一个引用,其本身也要占内存空间。
总结:
一个对象占用内存的大小算法,首先记住计算公式:对象头+实例数据+填充,一个非数组类型的空对象,即里面没有声明任何属性的对象,其理论大小只有对象头,在默认情况下是8+4=12个字节,但因为补齐填充所以最终是16个字节。