Java:第一次被 synchrnoized 上锁的对象的对象头到底怎么变?

以下内容均引自《深入理解 JVM 》第二版,周志明先生著。

简单地介绍了对象的内存布局后,我们把话题返回到轻量级锁的执行过程上。在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为“01”状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录( Lock record )的空间,用于存储锁对象目前的 Mark Word 的拷贝(官方把这份拷贝加了一个 Displaced 前缀,即 Displaced Mark Word ),这时候线程堆栈与对象头的状态如图 13-3 所示。 然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock record 的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位( Mark Word 的最后 2bit )将转变为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图 13-4 所示。

上面这段引用出自『 13.3.4 轻量级锁 』其中

(A)然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock record 的指针。

如果读者读懂了前面轻量级锁中关于对象头 Mark Word 与线程之间的操作过程,那偏向锁的原理理解起来就会很简单。假设当前虚拟机启用了偏向锁(启用参数-XX:+ UseBiased Locking,这是 JDK1.6 的默认值),那么,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为“01”,即偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中,如果 CAS 操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如 Locking 、Unlocking 及对 Mark Word 的 Update 等)

这一段出自『 13.3.5 偏向锁 』,其中

(B)那么,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为“01”(第一次被获取的话不是自然而然就是 01 么,为何还要特别设置),即偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中,

(A)中说的和(B)中说的更新到对象头的内容完全不是一个,其实它们对应的锁状态也不是一个,但从内容上看都是对第一次将因为 synchrnoized 被加锁的对象的对象头操作,那到底发生什么啊??

阅读 281
评论
    1 个回答

    我看了两个多小时,还把原书也扒出来看了,这个原书写的有问题,才会导致这两段意思这么混乱。一句话总结——这两个本来就是说的不同的标志位!!

    (B)那么,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设为“01”(第一次被获取的话不是自然而然就是 01 么,为何还要特别设置),即偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中,

    这个01是错误的,我查了stackoverflow和jdk的源码。你自己也可以看一下。HotSpot jvm Object Header 主要看37-42行和87-95行
    image.png

    32位的Object Header里面有一个bit专门记录是否可偏向,书中写的header的最后两个bit表示锁状态是没有问题的。但这里设为的不是“01”两个bit,只是把专门和偏向有关的标记位那一个bit设为1。
    image.png

    所以实际上,这个表就不该这么排列,上面四行都是最后两个bit的内容对应的状态,最后第五行是图中标红的那一个bit的状态,而且应该是“0/1”,用来表示是否可偏向。

    另外,这个表的意思也完全没有说明清楚,至少我看了很久才理解第二列标志位的值只和第三列的锁状态有关,而针对锁的状态不同,整个32bit的mark word除去锁的2个bit以外存储的内容是不一样的。
    image.png
    结合上面这个注释也可以更好理解一些,这个同样出自上面的源码链接。

    还有这个stackoverflow的post也很有干货,是关注同一个问题的。Details about mark word of java object header.
    最后吐槽两句,这两节的图在我查到这个源码和stackoverflow帖子之前也云里雾里。图倒是没错,但没有足够说明就看得人蛋疼。

      撰写回答

      登录后参与交流、获取后续更新提醒