Java对象内存布局详解

Java实例化之后,存储在堆内存中。其中主要分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

具体如下图


Java对象内存布局详解_第1张图片
对象组成

1 对象头

对象头主要有两部分组成:Mark Word 和 Klass word(Class Pointer) ;如果对象是数组,还需要有数组长度

1.1 Mark Word

主要用来存储对象自身运行时数据 如 hashcode,gc分代年龄 等信息,Mark位长度与系统保持一致,32位系统 则长度为32位4字节,64位系统则长度为64位8字节;

对象头格式 :


Java对象内存布局详解_第2张图片
32位格式


Java对象内存布局详解_第3张图片
64位格式

lock:2位的锁状态标记

biased_lock: 是否启用 偏向锁,1位。1启用 0不启用


Java对象内存布局详解_第4张图片
锁与偏向锁标志

age: 4位,Java对象年龄,Survivor复制一次,年龄+1;到达阈值晋升到老年代;默认情况下,并行GC阈值为15(4位最大是15),并发为6。-XX:MaxTenuringThreshold选项最大值为15,也是这个原因

thread: 持有偏向锁的线程ID

epoch: 偏向时间戳

identity_hashcode: 对象标识hash码,由System.identityHashCode()计算。对象被锁定时,转移到管程Monitor中

ptr_to_lock_record: 指向栈中锁记录的指针

ptr_to_heavyweight_monitor: 指向管程Monitor的指针

具体存储内容如下 

32位JVM


Java对象内存布局详解_第5张图片
32位JVM

64位JVM


Java对象内存布局详解_第6张图片
64位JVM存储

1.2 Klass Word(Class Poinnter)

klass类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例. 

如果应用对象过多时,使用64位指针比32位指针消耗更多内存,可以使用 +UseCompressedOops

开启指针压缩 节约内存;其中,oop即ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位:每个Class的属性指针(即静态变量)、每个对象的属性指针(即对象变量)、普通对象数组的每个元素指针

当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

1.3 对象头大小计算

1. 在32位系统下,存放Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节。 

2. 在64位系统下,存放Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节。 

3. 64位开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节。 数组长度4字节+数组对象头8字节(对象引用4字节(未开启指针压缩的64位为8字节)+数组markword为4字节(64位未开启指针压缩的为8字节))+对齐4=16字节。 

4. 静态属性所占用的空间通常不算在对象本身,因为它的引用是在方法区。


2 实例数据

对象真正存储的有效信息,各类型字段的内容

3 对齐填充

JVM要求 java对象占用内存必须是8 字节的倍数。如果整体占用不足8字节的倍数,则填充对应数量的占位符达到8字节倍数。

参考:

java对象头实现

JAVA 对象头解析

Java对象结构及大小计算

Java对象结构与锁实现原理及MarkWord详解

你可能感兴趣的:(Java对象内存布局详解)