本文的讨论都是基于 HotSpot 虚拟机
对象的内存布局
要知道一个类对象占用的内存,我们就必须要知道对象的内存布局。
对象在堆内存中的存储布局可以划分为三个部分: 对象头
,实例数据
,对齐填充
对象头
对象的对象头包括两类信息。第一类是存储对象自身的运行时数据,第二类是类型指针
Mark World
存储对象自身的运行时数据,如 哈希码,GC分代年龄、锁状态标志、线程持有的锁等等。这部分的数据长度在32位虚拟机中为4个字节,在64位虚拟机中是8个字节,官方称之为 Mark Word
类型指针
指向它的类型元数据的指针(指向它的Class对象的指针),大小是4个字节。Java虚拟机通过这个指针来确定该对象是哪个类的实例
实例数据
实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容。计算方式是累加,如下对象:
public class O {
private int o1;
private long o2
}
实例数据部分长度就是 int(4字节)+long(8字节)=12字节
对齐填充
第三部分就是对齐填充。HotSpot虚拟机的自动内存管理要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。如果对象大小没到8字节的整数倍,那就需要通过对齐填充来补全。
实战
已经知道了对象的内存布局,我们就可以来尝试计算一个类对象占用的内存:
我们就来计算 String类的内存:
空的String对象:
我们先来查看String类里面的实例数据有哪些;
@Stable
private final byte[] value;
private final byte coder;
private int hash; // Default to 0
private boolean hashIsZero; // Default to false;
private static final long serialVersionUID = -6849794470754667710L;
static final boolean COMPACT_STRINGS;
可以得到实例数据部分的字节是 数组对象 + byte(1字节)+ int(4字节)+ boolean(1字节) + long(8字节) + Boolean(1字节)
那么数组对象也是一个对象,它的占用内存是 对象头(8字节)+ 引用(4字节)+ 记录长度的int(4字节)=16字节。
所以空的String对象占用内存是 8+16+1+4+1+8+1+ 1字节(字节填充)=40字节
非空的String对象
非空的string对象比空的string对象只有在数组对象里的 实例数据部分变化了,其他都没变,
所以非空的String对象占用内存是 8+16+1+4+1+8+1+ 1字节(字节填充)=40+ n字节(n是byte数组的长度)