java对象头MarkWord探索

关于java对象头markword的文章有很多,基本都是说markword用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。具体是怎么存,怎么切换这些说的很少,这里使用hsdb深入跟踪了对象头markword,在这里记录下。

实验环境:

mac ox 10.12.6

jdk8 64-Bit

涉及工具:

jdb,hsdb

一. 局部变量生命周期 和 synchronized匿名锁执行过程

图1
图2

breakM()方法栈帧局部变量表元素:

1.lock对象

2.a变量

3.synchronized匿名monitor对象

4.b变量

5.synchronized匿名throwable对象

=》 第3个和第4个变量生命周期只在synchronized块中,所以出了synchronized块后,变量c放入局部变量表第3个位置,变量d放入局部变量表第4个位置。(只根据定义确认变量生命周期,不管在定义域内后面是否会使用)

说下这个主要是为了后面理解分析栈帧时,能清楚的知道栈帧局部变量表在每一行处时存储的值的意义。

二.  先找一段openjdk关于对象头markword描述的源码说明

图3

index[0,1]:锁标识/模式标识,一方面确定是否有锁,一方面确认字段解析方式

1.index[0,1] = 01 无锁

[header      | 0 | 01]  unlocked           regular object header

此时需要判断 index[2],是否有偏向标识

    1.1 index[2] = 0  无偏向,此时字段结构为:

    unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)

    1.2 index[2] = 1 偏向,此时字段结构为:

    JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)

    ==》

    //    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread

    //    [0           | epoch | age | 1 | 01]       lock is anonymously biased

2.index[0,1] = 00  轻量级锁

[ptr             | 00]  locked             ptr points to real header on stack

3.index[0,1] = 10  重量级锁

[ptr             | 10]  monitor            inflated lock (header is wapped out)

4.index[0,1] = 11  GC markSweep标志,标记对象不可用

[ptr             | 11]  marked             used by markSweep to mark an object not valid at any other time

三. 先看个简单的demo

public class TestHashCode{

    public static void main(String[] args){

        breakM();

    }

    public static void breakM(){

        LockBean lock = new LockBean();    //_mark = 0x0000000000000005    0101    无锁,任意偏向

        int a = lock.hashCode();                    //_mark = 0x0000007fbe847c01      0001    无锁,无偏向,hashcode

        synchronized(lock){                           //_mark = 0x0000700004b13870     0000    轻量级锁,栈顶指针

            int b = lock.hashCode();                //_mark = 0x0000700004b13870     0000

        }

        int c = lock.hashCode();                    //_mark = 0x0000007fbe847c01

        int d = 32;

    }

}

breakM方法里先定义了一个对象,再取该对象hashcode值,然后对该对象加锁。后面_mark表示执行完这一行后该对象的markword值,是很清晰的一段markword变更流程,我们下面做具体分析。

四. 轻量级锁执行过程分析

代码进入同步块时,如果此同步对象没有被锁定(markword锁标志为01),jvm首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的MarkWord拷贝(8byte存当前markword值拷贝,8byte存当前对象地址)。

=》如果此同步对象已被锁定(markword锁标志为00/10),则进入锁等待

然后尝试使用CAS将当前锁对象的MarkWord更新为指向Lock Record的指针。如果更新成功,则这个线程就获取了该对象的锁,并且该对象MarkWord的锁标识位(最后2bit)将转变为00,即表示该对象当前处于轻量级锁状态。

若更新操作失败,jvm会先检查该对象MarkWord是否已经是指向当前线程的栈帧,若是则说明已经获取过锁了,就直接进入同步块执行。否则说明当前锁对象已经被其他线程抢占了。

如果有多个线程同时争用同一个锁,此时轻量级锁要膨胀为重量级锁,锁标志位状态值变为10,Mark Word中存储的是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。(膨胀为重量级锁后,会生成一个新的锁Lock Record记录。锁对象markword指向新生成的重量级锁Lock Record记录,并把锁标志位置为10。)

轻量级锁的解锁也是通过CAS来做的,将Lock Record存储的之前markword值,CAS更新回锁对象的MarkWord中,更新成功则整个同步块完成;更新失败,则说明有其他线程尝试过获取该锁,那就要在释放锁的同时唤醒被挂起的线程。

=》多线程争用锁时,锁对象markword会变更为指向 新的重量级锁Lock Record地址;=》锁对象markword指向的重量级锁Lock Record地址 和 当前线程栈顶Lock Record地址不一致,说明锁膨胀为重量级锁了,存在多线程竞争。则当前线程释放锁,同时唤醒被挂起的线程。 

五. 单线程下加锁前后锁对象markword分析

图4

break1堆栈:

图5

当前markword = 0x0000007fbe847c01,无锁/无偏向/hashcode,栈帧顶部无 Lock Record 记录;

break2堆栈:

图6

当前markword = 0x000070000cd93868,指向栈顶新加的 Lock Record 记录;

Lock Record记录:8byte 存锁对象之前的markword值,8byte 存指向锁对象的指针;

=》单线程场景下,不存在任何锁竞争/CAS失败,一切复合预期;

六. 多线程下加锁前后锁对象markword分析

图7

6.1 main线程到break2,new线程到break1 堆栈:

图8

main线程获取锁,main线程当前栈帧为获取到锁记录的状态;new线程还未进入同步块;

当前锁对象的markword指向main线程栈顶Lock Record记录;

6.2 main线程先到break2(已获取锁,在同步块中未退出),new线程后到break2(尝试获取锁):

图9

可以看到,new线程进入同步块代码后,new线程的栈帧顶部也添加了 Lock Record 记录。

Q:=》new线程的 Lock Record前8byte存的值是 0x00..03,这个值是怎么来的?

A:=》最后2bit 11 在markword定义里表示对象处于GC mark不可用状态。在发生锁争用时锁膨胀为重量级锁,线程进入锁等待状态后,线程栈顶的Lock Record其实存在已经没有意义了。

main线程栈帧保持不变;

锁对象的markword值更新为 0x00007fcb19846dfa,既不指向main线程Lock Record,也不指向new线程Lock Record。

此时markword锁标志位为 10,意味着由于多线程获取锁,锁升级为 重量级锁 了,其他位的值,是指向重量级锁的指针值。

取其他位的值,去掉锁标志位的值并用00填充,得到重量级锁指针 0x00007fcb19846df8,查看该重量级锁记录,可以看到,也是存的一个Lock Record记录,前8byte存的锁对象之前无锁时的markword值,后8byte存的指向锁对象的指针;

Q:=》重量级锁Lock Record中前8byte存放的锁对象markword值,这个值是怎么取到或计算的?

A: 取的当前锁对象所指向的Lock Record记录中存放的markword值。

轻量级锁时,在同步块中运行对象hashcode方法后,由于要改变锁对象markword值(存储hashcode值),jvm的做法是:生成一个新的Lock Record记录(栈帧外),该新的Lock Record记录中存放锁对象含hashcode的markword值,然后将锁对象markwrd指向新的Lock Record记录;退出同步块时,也是从这里恢复锁对象markword值;

若已经是重量级锁,则运行锁对象hashcode方法后,将更新重量级锁Lock Record记录中的markword值为含hashcode的markword值。

6.3 main线程出了同步块,new线程在break2(已经获取到锁):

图10

main线程释放锁之后,new线程获取到锁,此时只有new线程占用锁,但是由于已经膨胀为重量级锁了,此时new线程获取的锁记录依然是这个重量级锁。

main线程释放锁后,main线程的栈顶Lock Record发生了一点变化,Lock Record中原本指向锁对象地址的指针,现在变为指向空地址的指针了。

6.4 main线程出了同步块,new线程出了同步块:

锁对象markword恢复成最后指向的Lock Record记录中前8byte中存储的锁对象进入同步块之前的markword值,由此,锁对象恢复到进入同步块之前的状态。


至此,对markword的含义有了更清晰的理解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容