Java对象内存布局详解

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


Java对象内存布局详解
文章图片
对象组成 1 对象头 对象头主要有两部分组成:Mark Word 和 Klass word(Class Pointer) ;如果对象是数组,还需要有数组长度
1.1 Mark Word

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


Java对象内存布局详解
文章图片
32位格式

Java对象内存布局详解
文章图片
64位格式 lock:2位的锁状态标记
biased_lock: 是否启用 偏向锁,1位。1启用 0不启用


Java对象内存布局详解
文章图片
锁与偏向锁标志 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对象内存布局详解
文章图片
32位JVM 64位JVM


Java对象内存布局详解
文章图片
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对象内存布局详解】Java对象结构与锁实现原理及MarkWord详解

    推荐阅读