Java对象与JVM(二)
Java对象在Java虚拟机中的内存布局
在前面《Java对象在Java虚拟机中的创建过程》文章了解到Java对象实例是如何在Java堆中创建的。
下面我们详细了解Java普通对象创建后,在HotSpot虚拟机Java堆中的内存布局是怎样的,可以分为3个区域:对象头(Header)、实例数据(Instance)和对齐填充(Padding)。
1、对象头
可以主要分为两部分:
(A)、存储对象自身运行时数据
称为"Mark Word";
包括哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;
这部分长度为32bit(32位JVM)或64bit(64位JVM);
被设计成一个非固定的数据结构,会根据对象的状态复用自己的存储空间,以便在极小的空间内存储尽量多信息;
例如,32bit的Mark Word在未被锁定状态下,前25bit存储对象哈希码,4bit存储对象分代年龄,2bit存储锁标志位,1bit固定为0,如图所示
(B)、存储指向对象类型数据的指针
通过这个指针确定这个对象是哪个类的实例;
不是必须的,看对象的访问定位方式:
对HotSpot虚拟机来说,由于JVM栈本地变量表中对象的reference类型引用使用直接指针,该指针指向堆内存中的对象,所以对象头中是需要存储它的类元数据指针,该指针指向方法区中对象类型数据。
(C)、如果是Java数组,对象头还需要存储数组长度
因为数组对象类型数据中没有数组长度信息;
而JVM可以通过普通Java对象的类元数据信息确定对象大小;
在HotSpot虚拟机源码"markOop.hpp"注释中,对对象头信息存储有明确的说明:
// Bit-format of an object header (most significant first, big endian layout below): // // 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) // // 64 bits: // -------- // unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) // PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) // size:64 ----------------------------------------------------->| (CMS free block) // // unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) // JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) // narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object) // unused:21 size:35 -->| cms_free:1 unused:7 ------------------
不同锁标志说明如下:
// // [ptr | 00] locked ptr points to real header on stack // [header | 0 | 01] unlocked regular object header // [ptr | 10] monitor inflated lock (header is wapped out) // [ptr | 11] marked used by markSweep to mark an object // not valid at any other time
2、实例数据
对象真正存储的有效信息;
程序代码所定义的各种类型字段内容,以及包括父类继承或子类定义的;
存储顺序:
受到JVM分配策略参数(FiedAllocationStyle)和字段在Java源码中定义顺序影响;
JVM默认分配策略为:longs/double、ints、short/char、boolean、oops(Ordiary Object Pointers);
VM默认分配策略使得,相同宽度的字段总被分配到一起;
这个前提下,父类定义的变量出现在子类之前;
如果虚拟机的"CompactFields"参数为true,子类中较窄的变量可能插入到父类变量空隙中,以压缩节省空间;
3、对齐填充
不是必然存在的;
只起占位符作用,没有其他含义;
HotSpot虚拟机要求对象大小必须是8字节的整数倍;
对象头是8字节整数倍,所以填充是对实例数据没有对齐的情况来说的;
到这里,我们大体了解Java对象在HotSpot虚拟机中的内存布局, 后面我们将分别去了解:对象的访问定位、方法的调用与执行、JIT编译、以及JVM垃圾收集相关内容……
【参考资料】
1、《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版 第2章
2、《The Java Virtual Machine Specification》Java SE 8 Edition:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
3、《Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide》:http://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html
4、《Memory Management in the Java HotSpot™ Virtual Machine》:http://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf
5、HotSpot虚拟机参数官方说明:http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html