Java并发编程问题汇总二:对象布局

Page content

20200727135202

对象在内存中分为三大部分:对象头、实例数据、对齐填充。

而对象头又分为两部分:MarkWord,类型指针。

今天的主角就是MarkWord,包含了对象运行时的信息:hash、gc分代年龄、锁状态、线程持有的锁,偏向锁id等等。

MarkWord与锁

Mark Work的每个标志位,并不是固定的含义。为了保证内存使用的高效,MarkWord被设计成了非固定的设据结构。根据最后两个bit,来确定目前对象处于什么状态,从而确定其他位代表什么意义。

例如,当锁标志位是01的时候,表示未锁定,前25bit表示对象的hash,接下来4个bit表示分代年龄,等等等等。

对象上锁的过程如下:

  1. 当前对象没有被锁定,锁标志位是01. 当对象进入同步块时(当前对象是01未锁定),虚拟机首先在栈帧上建立一个名字叫Lock Record的空间,存储当前MarkWord的copy。
  2. 虚拟机尝试使用CAS的方式,将MarkWord更新为指向LockRecord的指针,并将锁标志更新为00,如果成功了,则对象处于轻量锁定状态。
  3. 如果没有成功,则检查是否当前对象的MarkWord指向当前线程的LockRecord,如果是,则重入即可。
  4. 否则表明多个线程在争抢对象,这个时候,锁标志位变为10,也就是膨胀为重量级锁。其他线程也就进入阻塞状态。

偏向锁

JDK1.6之后,为了进一步增加性能,引入了偏向锁。

偏向锁跟无锁的锁标志一样,不同的是倒数第三个bit。当这个bit是0的时候,表示是无锁,如果是1,表示是课偏向锁。

过程如下:

  1. 当线程第一次获取到这个对象时,使用CAS将MarkWrod设置为偏向状态,并把线程ID写入到钱23bit中,后面都不会再出现cas操作了。
  2. 知道另一个线程尝试获取,JVM根据对象是否处于锁定状态,决定是否撤销偏向锁或者改成轻量级锁。