目录
Java对象存储:(HOTSPOT虚拟机)
一个Java对象可以分为三部分存储在内存中,分别是:
- 对象头Object Header
- 实例数据Instance
- 对齐填充Padding
什么是对象头,具体包括什么
在HotSpot虚拟机中,对象头分为两部分(普通对象)
- 1.对象自身的运行时数据: 包括存储哈希码,GC分代年龄,锁状态标记,线程持有的锁,偏向锁ID,偏向时间戳等.这部分被叫做mark word,在32位和64位的虚拟机分别占32bits和64bits.
- 2.对象的类型指针: 即指向对象的类元数据的指针.虚拟机可以通过该指针判定对象实例属于那个类.
注:在Java对象中比较特殊的是数组,一个数组实例的对象头中必须包含数组的长度.JVM可以通过对象头中的数组长度数据来判定数组的大小,这是访问数组类型的元数据是无法得知的
JVM中对象头有两种:
1.普通对象
Object Header: 包括mark word和class pointer
2.数组对象
Object Header : 包括mark word,class pointer和array length
数组特有的length ,JVM需要通过数组对象头的length来判定数组的大小.
Mark Word
64位虚拟机
32位虚拟机
Mark Word分析 (64位):
-
对象的hashcode(identity hash code):
占31bit,根据内存地址生成的hash值. -
GC分代年龄:
age 占4bits,在GC过程中,如果新生代对象在Survivor区复制一次,年龄+1.当对象年龄达到设定的阈值时,对象将会进入老年代.或者有一半的的对象年龄相等,则大于该年龄的对象直接进入老年代.默认情况GC年龄阈值为15.因为age只占4位 ,所以最大值为1111= 15. -
锁标志位: 占2bit位,希望用尽可能少的二进制位表示尽可能多的信息,所以设置了锁的标记.该标记的值不同,整个mark word 表示的含义不同.
-
biased_lock(是否为偏向锁) : 对象是否为偏向锁,只占1个bit. 为1时表示对象启用偏向锁,为0表示对象没有偏向锁.
无锁和偏向锁的锁标志都是01 ,区分的方式是通过1bit的是否为偏向锁(biased_lock).偏向锁的这1bit等于1,无锁这一位则为0
-
锁的状态
-
new (未锁定状态)
在一个对象刚new出来的时候,没有线程竞争,就是一个普通对象,不需要加锁,此时biased_lock为1,锁的标志位为01 , 表示无锁可偏向 . ( 如果计算了对象的hashcode ,则会记录对象的hashcode,锁的标志位为01.biased_lock位置为0. 此时表示无锁不可偏向状态) -
偏向锁:biased_lock
如果已经计算了对象的hashcode ,则表示该锁不能偏向 .直接升级为轻量级锁.(对象的hashcode和偏向线程id只能存储一个)
1.当第一个线程A来获取资源的时候,这个时候只有线程A一个,没有其他线程来竞争,他会将biased_lock标志位置为1,锁标志为01, 表示已经偏向它的状态.线程ID也会记录A的id.
2.当A线程再次获取该资源的时候,JVM发现mark word里面的线程id是A的id,锁的标志位是01,biased_lock是1,表示A已经获得该偏向锁. -
轻量级锁(自旋,自旋锁,look-free,CAS轻量级锁):
当线程B尝试获取该锁时(此时有了锁的竞争),JVM发现此时锁处于偏向状态,mark word的线程id记录的是A,此时线程B会尝试通过CAS的方式(在用户空间)获取锁.两个线程都将对象的hashcode复制到自己新建的用于存储锁的记录空间LockRecord,通过CAS的操作,将对象的mark word的内容修改为自己新建的记录空间的地址来竞争mark word.成功则获取资源,失败则继续CAS操作.
自旋的线程在自旋过程中,成功获取资源,整个状态仍然处于轻量级锁的状态. -
重量级锁:
竞争加
-