本篇内容介绍了“java对象的内存布局分为哪几个区域”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
HotSpot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
1)对象头:包括标记字段和类型指针两部分内容(注:如果是数组对象,则包含三部分内容):
1)Mark Word(标记字段):用于存储运行时对象自身的数据。 1>占用内存大小与虚拟机位长一致,在运行期间,考虑到JVM的空间效率,Mark Word被设计成为一个非固定的数据结构,以便存储更多有效的数据。 2>存储运行时对象自身的数据: 哈希码(hash) GC分代年龄(age) 锁标识位: 01 无锁 01 偏向锁 00 轻量级锁 10 重量级锁 偏向锁标识位(biased_lock) 0 无锁 1 偏向锁 偏向线程ID(JavaThread*) 偏向时间戳(epoch) 说明:锁标识位、偏向锁标识位、偏向线程ID等的具体实现均是在monitor对象中完成的。(源码中的ObjectMonitor对象) 3>Mark Word里存储的数据会随着锁标志位的变化而变化,即不同的锁状态,存储着不同的数据: 锁状态 存储内容 锁标识位 偏向锁标识位(是否是偏向锁) -------- ------------------------------ -------- -------- 无锁状态 哈希码、GC分代年龄 01 0 偏向锁 线程ID、偏向时间戳、GC分代年龄 01 1 轻量级锁 指向栈中锁记录的指针 00 无 重量级锁 指向monitor的指针 10 无 GC标记 无 11 无 2)Class Metadata Address(类型指针):指向对象的类元数据(方法区的Class数据),虚拟机通过这个指针确定该对象是哪个类的实例。 3)如果对象是数组类型,则对象头中还存储着数组的长度。
2)实例数据:存放类的属性数据信息,包括父类的属性信息。
3)对齐填充:由于虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的,仅仅是为了字节对齐。
4)使用JOL(Java Object Layout)工具查看java对象的内存布局:
maven依赖: <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.14</version> </dependency> 代码: public class TestClassLayout { public static void main(String[] args) { TestClass[] testClassObj = new TestClass[5]; // 查看对象内存布局 System.out.println(ClassLayout.parseInstance(testClassObj).toPrintable()); } } 结果: 1 [Lcom.jxn.test.TestClass; object internals: 2 OFFSET SIZE TYPE DESCRIPTION VALUE 3 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 5 8 4 (object header) 82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190) 6 12 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 7 16 20 com.jxn.test.TestClass TestClass;.<elements> N/A 8 36 4 (loss due to the next object alignment) 9 Instance size: 40 bytes 10 Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 对象头: 第3行+第4行: Mark Word(标记字段) 第5行: 类型指针 第6行: 数组长度 实例数据: 第7行: 数组中存储了5个TestClass对象的引用(开启指针压缩后,类型引用占4个字节),故占用5*4=20个字节。 对齐填充: 第8行: 对象头+实例数据 占用的内存为36字节(不是8的整数倍),故需要4字节的对齐填充。 对象占用的总空间: 第9行 内存浪费的总空间: 第10行: 对齐填充消耗了4个字节。 说明:若数组的长度改为6,则 对象头+实例数据 占用的内存为40字节(8的整数倍),故不会出现对齐填充: [Lcom.jxn.test.TestClass; object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 82 c1 00 f8 (10000010 11000001 00000000 11111000) (-134168190) 12 4 (object header) 06 00 00 00 (00000110 00000000 00000000 00000000) (6) 16 24 com.jxn.test.TestClass TestClass;.<elements> N/A Instance size: 40 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
5)hotspot/src/share/vm/oops/markOop.hpp 源码中的说明:
// The markOop describes the header of an object. // // Note that the mark is not a real oop but just a word. // It is placed in the oop hierarchy for historical reasons. // // 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 ------------------>| (COOPs && CMS free block) // // - hash contains the identity hash value: largest value is // 31 bits, see os::random(). Also, 64-bit vm's require // a hash value no bigger than 32 bits because they will not // properly generate a mask larger than that: see library_call.cpp // and c1_CodePatterns_sparc.cpp. // // - the biased lock pattern is used to bias a lock toward a given // thread. When this pattern is set in the low three bits, the lock // is either biased toward a given thread or "anonymously" biased, // indicating that it is possible for it to be biased. When the // lock is biased toward a given thread, locking and unlocking can // be performed by that thread without using atomic operations. // When a lock's bias is revoked, it reverts back to the normal // locking scheme described below. // // Note that we are overloading the meaning of the "unlocked" state // of the header. Because we steal a bit from the age we can // guarantee that the bias pattern will never be seen for a truly // unlocked object. // // Note also that the biased state contains the age bits normally // contained in the object header. Large increases in scavenge // times were seen when these bits were absent and an arbitrary age // assigned to all biased objects, because they tended to consume a // significant fraction of the eden semispaces and were not // promoted promptly, causing an increase in the amount of copying // performed. The runtime system aligns all JavaThread* pointers to // a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM)) // to make room for the age bits & the epoch bits (used in support of // biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs). // // [JavaThread* | epoch | age | 1 | 01] lock is biased toward given thread // [0 | epoch | age | 1 | 01] lock is anonymously biased // // - the two lock bits are used to describe three states: locked/unlocked and monitor. // // [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 // // We assume that stack/thread pointers have the lowest two bits cleared.
“java对象的内存布局分为哪几个区域”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。