Java 对象布局及其组成
在 hotspot 虚拟机中,对象在内存中布局可以被分为三部分:对象头/实例数据/补位数据。下面一张图是一个普通 java 对象和一个数组对象的结构组成:
Java 对象组成
Hotspt 采用了 OOP-Klass 模型。 它是描述 java 对象实例的模型,可分为两部分:
- OOP (Ordinary Object Pointer)指的是普通对象指针,它包含 MarkWord 和Klass 指针。MarkWord 用于存储当前对象运行时的一些状态数据;Klass 指针则指向 Klass,用来告诉当前指针指向的对象是什么类型,即对象是使用哪个类创建出来的
- 之所以采用这种一分为二的对象模型,是因为 hotspot jvm 的设计者不想让每个对象中都包含一个 virtual table (虚函数表), 所以把对象模型拆成 klass 和 oop,其中 oop 不包含任何虚函数,而 klass 含有虚函数表,可以进行method dispatch
对象的模型如下:
volatile markOop _mark; //标识运行时数据 union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata; //klass指针
对象头
对象头主要有两部分(数组对象有三组分)组成。 Markword, Klass 指针(数组对象的话,还有一个 length)。
MarkWord
标记字主要存储对象运行时的一部分数据。主要内容有 hashcode,GC 分代年龄,锁状态标志位,线程锁标记,偏向线程ID,偏向时间戳等。MarkWord 在32位和64位虚拟机上的大小分别位32bit 和 64bit,它的最后 2 bit 是锁标志位,用来标记当前对象的状态,具体如下:
状态 | 标志位 | 存储内容 |
---|---|---|
未锁定 | 01 | 对象哈希码/对象分代年龄 |
轻量级锁定 | 00 | 指向锁记录的指针 |
膨胀(重量级锁定) | 10 | 执行重量级锁定的锁指针 |
GC 标记 | 11 | 空(不需要记录信息) |
可偏向 | 01 | 偏向线程id, 偏向时间戳,对象分代年龄 |
32 位 vm 在不同状态下 Markword 结构如下:
Klass 指针(元数据指针)
Klass 主要指向对象所属的类信息(class metadata)。虚拟机使用它来确定当前对象属于哪个类。klass 包含类的元数据信息,像类的方法,常量池等。你可以把它当作 java 里的 java.lang.Class 对象
数组长度
如果这个对象是数组类对象,那么如上图右侧所示,会在对象头里额外的添加一个记录数组长度的字段
实例数据
这部分主要存储的是对象实际的数据
对齐填充
hotspot vm 的自动内存管理系统要求对象起始地址比必须位 8 字节的整数倍,即对象的大小必须是 8 字节的整数倍。而对象头部分正好是 8 字节的整数倍,因此,当对象实例数据部分没有对齐时,此时需要对齐填充来补齐
Java 对象大小计算
首先,对象头大小的确定
- 在32位系统中,存放 Class 的指针空间大小为 4 字节,MarkWord 为 4 字节,即对象头为 8 字节
- 在64 位系统中,存放 Class 指针的空间大小为 8 字节, MarkWord 为 8 字节,即对象头大小为 8 字节
- 64 位系统下,如果开启了指针压缩,存放 Class指针的空间大小为 4 字节,MarkWord 为 8 字节,即对象头为 12 字节
- 对于对象数据结构,参照上述三个规则,额外加上一个数组长度所含用的 4 个字节,就是最终对象头的大小
- 计算对象的大小时,静态数据是不会被考虑进去的
有很多中方法计算,这里我们使用 jol 进行计算
首先假设我们待计算的对象所属的 Class 结构如下:
public class JOLSample_10_DataModels { /* * This example shows the differences between the data models. * * First layout is the actual VM layout, the remaining three * are simulations. You can see the reference sizes are different, * depending on VM bitness or mode. The header sizes are also * a bit different, see subsequent examples to understand why. */ public static void main(String[] args) throws Exception { Layouter l; l = new CurrentLayouter(); System.out.println("***** " + l); System.out.println(ClassLayout.parseClass(A.class, l).toPrintable()); l = new HotSpotLayouter(new X86_32_DataModel()); System.out.println("***** " + l); System.out.println(ClassLayout.parseClass(A.class, l).toPrintable()); l = new HotSpotLayouter(new X86_64_DataModel()); System.out.println("***** " + l); System.out.println(ClassLayout.parseClass(A.class, l).toPrintable()); l = new HotSpotLayouter(new X86_64_COOPS_DataModel()); System.out.println("***** " + l); System.out.println(ClassLayout.parseClass(A.class, l).toPrintable()); } public static class A { Object a; Object b; } }
其在不同环境下的内存布局如下:
参考:
来源:https://www.cnblogs.com/neocxf/p/12600114.html