美文网首页
Java对象头与锁

Java对象头与锁

作者: 抬头挺胸才算活着 | 来源:发表于2020-06-02 15:53 被阅读0次

    参考资料:
    https://stackoverflow.com/questions/26357186/what-is-in-java-object-header

    普通对象

    数组对象

    其中 Mark Word 结构为

    64 位虚拟机 Mark Word

    偏向锁头部

    开始头部的hashcode、age都为0、biased_lock都为1,所以是0x00..101
    加锁之后前面的改为存放threadid,标志位还是01,不变
    解锁之后还是存放着threadid

    11:08:58.117 c.TestBiased [t1] - synchronized 前
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101
    11:08:58.121 c.TestBiased [t1] - synchronized 中
    00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101
    11:08:58.121 c.TestBiased [t1] - synchronized 后
    00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101
    

    轻量级锁头部

    开始头部的hashcode、age都为0、biased_lock都为0,所以是0x00..101
    前面指向栈帧中的锁记录的地址,标志位改为00
    退出锁之后跟进入之前是一样的

    11:13:10.018 c.TestBiased [t1] - synchronized 前
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
    11:13:10.021 c.TestBiased [t1] - synchronized 中
    00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000
    11:13:10.021 c.TestBiased [t1] - synchronized 后
    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
    

    调用对象 hashCode

    调用了对象的 hashCode,但偏向锁的对象 MarkWord 中存储的是线程 id,如果调用 hashCode 会导致偏向锁被撤销。
    轻量级锁会在锁记录中记录 hashCode
    重量级锁会在 Monitor 中记录 hashCode

    因为调用了hashcode,所以加锁的是轻量级锁,跟上面轻量级锁是一样的。
    轻量级锁会把hashcode等放在锁记录,所以解锁之后还是可以恢复hascode。

    11:22:10.386 c.TestBiased [main] - 调用 hashCode:1778535015
    11:22:10.391 c.TestBiased [t1] - synchronized 前
    00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001
    11:22:10.393 c.TestBiased [t1] - synchronized 中
    00000000 00000000 00000000 00000000 00100000 11000011 11110011 01101000
    11:22:10.393 c.TestBiased [t1] - synchronized 后
    00000000 00000000 00000000 01101010 00000010 01001010 01100111 00000001
    

    当有其它线程使用偏向锁对象时(先后,不是同时),会将偏向锁升级为轻量级锁

    t1加偏向锁(标志位为01),然后解锁,锁解完之后还是偏向于t1,看第二行前面threadid还是只想t2。
    接着t2加锁,升级为轻量级锁(标志位为00)
    解锁之后,markword变回hashcode那些。

    [t1] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101
    [t2] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101
    [t2] - 00000000 00000000 00000000 00000000 00011111 10110101 11110000 01000000
    [t2] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
    

    调用 wait/notify 锁变为重量级锁

    第一行是加锁前,轻量级锁
    第二行加锁之后,前面为threadid,t1进入wait
    第三行t2调用notify
    第四行t1变为重量级锁,头部指向monitor

    [t1] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101
    [t1] - 00000000 00000000 00000000 00000000 00011111 10110011 11111000 00000101
    [t2] - notify
    [t1] - 00000000 00000000 00000000 00000000 00011100 11010100 00001101 11001010
    

    批量重偏向

    如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程 T1 的对象仍有机会重新偏向 T2,重偏向会重置对象的 Thread ID。
    当撤销偏向锁阈值超过 20 次后(这里不是针对同一个加锁对象,而是任何加锁对象),jvm 会这样觉得,我是不是偏向错了呢,于是会在给这些对象加锁时重新偏向至加锁线程。其中前19次都是,将偏向锁改为轻量级锁。

    批量撤销

    当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的

    相关文章

      网友评论

          本文标题:Java对象头与锁

          本文链接:https://www.haomeiwen.com/subject/qqguzhtx.html