前言:本文基于64位openjdk1.8 hotspot源码展开。
想必各位Javaer对于对象应该都不陌生吧,从开始学java到步入工作,需要的时候都会自己new一个。但是大家对于对象的理解又有多少呢?今天,让我们一起来深入了解对象。
什么是对象?
我们先来看看hotspot源码中,对于对象的描述,源码位于:hotspot\src\share\vm\oops\instanceOop.hpp
大致意思就是说oopDesc 是对象类的顶级基类,也就是java中所有的对象都可以通过oopDesc进行描述。我们来看看这个类究竟长什么样?
对象布局
private:
// MarkWord
volatile markOop _mark;
// 类型指针 可能是普通直接指向MetaSpace中Klass的指针,也可能是压缩过后的指针
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
从源码中我们可以看到两块关键的信息:MarkWord以及类型指针,而对象的数据等到使用的时候才会去进行填充.
为了更好的理解对象,这里在网上找了一张图,可以说明对象的布局:
对象头
markOop 也即是我们熟悉的markWord,存储着诸如:对象年龄、hashCode等跟对象相关的信息
Markword对应的源码位于:hotspot\src\share\vm\oops\markOop.hpp
主要关注其注释的部分,这些注释写的非常详细,给我们提供了很多有用的信息,描述了32位跟64位的大端布局
// 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: 32位对象布局
// --------
// --普通状态下的对象头布局
// 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:
// -------- 64位下未开启指针压缩时的对象布局
// --普通状态下的对象头布局
// 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)
//
// -------- 64位下开启指针压缩时的对象布局
// --普通状态下的对象头布局
// 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.
因为我们现在接触的大多是64位的jdk,所以,下文如无特殊说明,均以64位JDK作为描述的范围。
64位JDK下的MarkOop存储的内容如下:
注释中只说明了普通对象、偏向锁时的对象、升级到老年代、被GC标志的对象的布局,但是涉及到偏向锁升级为轻量级锁、重量级锁时对象的状态并没有描述。下面的表格对这几种状态下的布局进行说明,等下也可以通过HSDB对这一过程进行证明。
开启了压缩指针的情况下:
| 锁状态 | MarkWord:64位长度分配 | |||||
|---|---|---|---|---|---|---|
| 无锁 | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
| 偏向锁 | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 |
| 轻量级锁 | ptr_to_lock_record:62 | lock:2 | ||||
| 重量级锁 | ptr_to_heavyweight_monitor:62 | lock:2 | ||||
| 对象升级 | narrowOop:32 | unused:24 | cms_free:1 | unused:4 | promo_bits:3 | |
| 垃圾回收 |
关闭压缩指针的情况下:
| 锁状态 | MarkWord:64位长度分配 | |||||
|---|---|---|---|---|---|---|
| 无锁 | unused:25 | identity_hashcode:31 | cms_free:1 | age:4 | biased_lock:1 | lock:2 |
| 偏向锁 | thread:54 | epoch:2 | cms_free:1 | age:4 | biased_lock:1 | lock:2 |
| 轻量级锁 | ptr_to_lock_record:62 | lock:2 | ||||
| 重量级锁 | ptr_to_heavyweight_monitor:62 | lock:2 | ||||
| 对象升级 | PromotedObject*:61 | promo_bits:3 | ||||
| 垃圾回收 |
对象头锁标志位
| 锁标志位值 | 含义 |
|---|---|
| 00 | 加锁 |
| 01 | 普通对象 |
| 10 | 重量级锁 |
| 11 | 垃圾标记 |
到此,我们算是对熟悉的对象的大概布局以及对象有了一定的了解。在对象头里,也介绍了我们熟悉的锁升级过程对应的对象头的变化。尽管很多jvm书籍告诉我们,锁升级的过程就是这样这样,但真的如此吗?
欲知后事如何,且听下回分解