Java对象的存储布局以及锁状态锁升级过程

 

Java对象在内存中的结构(非数组)
对象头 markword(32位JVM占32位,64位JVM占64位)
class pointer(32位JVM占32位,64位JVM在未开起压缩指针时占64位,开启时占32位):object.Class(如String.Class)
如果是数组,这里存数组长度(数组长度占32位),如果不是,则没有这部分
instance data 对象属性(以实际对象内属性为准)
padding java内存地址按照8bytes对齐,长度必须是8的倍数

 

markword 以及锁升级过程(32位JVM)
锁状态 25bit 4bit 1bit 2bit
23bit 2bit 是否偏向锁 锁标志位
无锁状态 对象的hashCode 分代年龄 0 01
偏向锁 线程id Epoch 分代年龄 1 01
轻量级锁 指向栈中锁记录的指针 00
重量级锁 指向重量级锁的指针 10
GC标记 11

无锁状态:前25位存的是对象的hashcode--identity hashCode,26--29 位存的是分代年龄(最大分分代年龄2^4 -1 =15),第30位存储偏向锁标志,第31-32位存储的是锁标志位; 

1、一个对象刚刚new出来的时候处于无锁状态,锁标志位为:01 ,偏向锁标志为: 0 ,|| identity hashCode || GC age || 0 || 01 ||;

2、当第一个线程锁定该对象时,会检查 锁标志位 是否是01 ,然后检查 偏向锁标志位是否为 0,CAS设置线程ID和偏向锁标志位,会进入偏向锁状态:|| 线程ID || Epoch || GC age || 1 || 01 ||  ,偏向锁不是并发,线程重复加锁,由于是偏向于这个线程的,markword记录线程ID和当前线程ID一样,所以直接获得偏向锁;

3、偏向锁被争用的时候回升级为轻量级锁,当第二个线程试图锁定该对象时,会发生争抢,锁状态升级为轻量级锁,轻量级锁实现原理:当线程争用这个锁的时候,会在线程的栈里面建立一个锁记录-LockRecord ,原来的markword (包括hashCode)数据全部复制到了这个LockRecord中,原来的 || 线程ID || Epoch || GC age ||  会被替换成指针,指向这个LockRecord ,然后得到这把偏向锁,过程:

|| 线程ID || Epoch || GC age || 1 || 01 || --> stack上建立LockRecord --> copy markword 到 LockRecord --> CAS 替换 markword 的lockRecod指针 --> || LockRecord Pointer || 01 ||;

4、轻量级锁升级为重量级锁:发生自旋超过10次,或者等待线程超过CPU核数的一半;

Object o = new Object();

其中的o 占4个字节,new Object()在:

32位JVM占8字节:markword:32位,classpointer:32位,如果没有实例数据instance,不需要对齐padding ,最小占用8Byte存储空间;

64位JVM,markword占64位 ,class pointer在不开启压缩存储情况下占64位(开启占32位),没有其他实例属性时,不需要要对齐,占16字节,开启压缩指针占12字节,对齐4个字节,所以也是16字节;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章