Java对象头
锁存在Java对象头里
如果对象是数组类型,则虚拟机用3 Word储存对象头
如果对象是非数组类型,则虚拟机用2 Word储存对象头
32 bit JVM中,一字宽 = 32 bit= 4 byte
长度 | 内容 | 说明 |
---|---|---|
32/64 bit | Mark Word | 储存对象的hashCode和锁信息 |
32/64 bit | Class Metadata Address | 储存对象类型数据的指针 |
32/64 bit | Array Length | 数组的长度(如果当前的对象是数组) |
25 bit | 4 bit | 1 bit(是否为偏向锁) | 2 bit(锁标志位) | |
---|---|---|---|---|
无锁状态 | 对象的hashCode | 对象分代年龄 | 0 | 01 |
运行期间Mark Word的储存的数据会随着锁标志位变化而变化
Mark Word有以下四种状态变化:
锁状态 | 25 bit | 4 bit | 1 bit | 2 bit | |
---|---|---|---|---|---|
23 bit | 2 bit | 是否为偏向锁 | 锁标志位 | ||
轻量级锁 | 指向栈中锁记录的指针 | 00 | |||
重量级锁 | 指向互斥量(重量级锁)的指针 | 10 | |||
GC标记 | 空 | 11 | |||
偏向锁 | 线程ID | Epoch(偏向锁的时间戳) | 对象分代年龄 | 1 | 01 |
自旋锁:
线程的阻塞和唤醒需要CPU从用户态转为核心态,过于频繁的转换对于CPU是个过重的负担,同时我们知道有些时候锁的锁定状态只会持续很短的一段时间,像是整数的自加操作,因此加入了自旋锁。
所谓的自旋就是让线程去执行无意义的循环,循环结束再去竞争锁,如果竞争不到继续循环,循环过程中线程会一直处于running状态,但是基于JVM的线程调度,会出让时间片,所以其他线程依旧有申请锁和释放锁的机会。
自旋的次数一般控制在一个范围内,例如10次、1次0次等,在超出这个范围后,自旋锁会升级为阻塞锁。
锁消除:
锁消除是指虚拟机即时编译器在运行时,一些代码要求同步,但是被检测到不可能存在共享数据竞争的锁就会被削除。
锁偏向:
只有一个线程进入临界区,目的是使某个线程获得锁之后,消除CAS Mark Word的开销,让这个线程得到偏护。
轻量级锁:
多线程交替进入临。