简介
偏向锁
Java偏向锁(Biased Locking)是Java 6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。不启用偏向锁的情况下,使用synchronize原语对对象进行加锁,使用的是轻量级锁,此时如果发生锁重入,那么需要在当前栈帧中创建LockRecord区域,产生性能开销。
HashCode
hashCode就是对象的散列码,是根据对象的某些信息推导出的一个整数值,默认情况下表示是对象的存储地址。由于堆中的对象位置不固定,会因为垃圾回收的缘故导致位置变化,所以对象的HashCode(未覆写的情况下)方法在第一次调用时需要存储起来,以保证同一个对象在任何时候调用HashCode返回相同的值。
补充
下图为对象头在各种状态下的MarkWord的结构
从图中可以看出,锁正常状态下,HashCode直接存储在对象头。
在轻量级锁中,HashCode存储在线程栈帧的Lock Record对象中
在重量级锁中,HashCode依然存在栈帧的Lock Record中,只不过对象头不再直接指向Lock Record,而是要先访问Monitor对象的Owner指针。综上所述,在对象的各种状态中,唯独偏向锁没有合适的地方存储HashCode,那么当处于偏向锁时,此时又调用HashCode会发生什么呢?
测试
测试程序
package com.test.lock;
import org.openjdk.jol.info.ClassLayout;
import java.util.concurrent.TimeUnit;
public class BiasedLock {
public static void main(String[] args) throws InterruptedException {
Object a = new Object();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
a.hashCode();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
synchronized (a) {
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
System.out.println(ClassLayout.parseInstance(a).toPrintable());
System.out.println("---------------------------");
System.out.println("---------------------------");
System.out.println("---------------------------");
Object b = new Object();
System.out.println(ClassLayout.parseInstance(b).toPrintable());
synchronized (b) {
System.out.println(ClassLayout.parseInstance(b).toPrintable());
b.hashCode();
System.out.println(ClassLayout.parseInstance(b).toPrintable());
}
System.out.println(ClassLayout.parseInstance(b).toPrintable());
TimeUnit.SECONDS.sleep(1L);
System.out.println(ClassLayout.parseInstance(b).toPrintable());
synchronized (b) {
System.out.println(ClassLayout.parseInstance(b).toPrintable());
}
}
}
- 运行时会启用偏向锁,且启动延迟时间设为0(-XX:BiasedLockingStartupDelay=0)。
- 启用安全点日志输出(-Xlog:safepoint)用于观察锁降级现象。
- maven依赖配置如下:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
测试步骤
- 启用偏向锁之后的MarkWord,调用HashCode之后,加锁时、加锁结束。
- 启用偏向锁之后的MarkWord,加锁时、加锁时并调用HashCode、加锁结束、睡眠1s后再观察、再次尝试加锁。
测试步骤1的结果
启用之后的MarkWord,可以看到转换二进制之后是101,表示偏向锁启用状态。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
调用hashCode之后,二进制的后两位是01,表示无锁状态,说明偏向锁被撤销。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000006379eb01 (hash: 0x006379eb; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁时,二进制的后两位是00,表示轻量级锁,说明无锁状态下加锁,默认就是轻量级锁。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000016fa66ab0 (thin lock: 0x000000016fa66ab0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁结束后、MarkWord 恢复到和无锁状态一样的值,说明变回了无锁状态,而不是偏向锁。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000006379eb01 (hash: 0x006379eb; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
测试步骤2的结果
启用之后的MarkWord,可以看到转换二进制之后是101,表示偏向锁启用状态。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000005 (biasable; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁时,可以看到MarkWord中添加了线程Id的信息,处于偏向锁使用中状态。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000128008805 (biased: 0x00000000004a0022; epoch: 0; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁时调用HashCode,可以看到转换二进制之后的尾数是10,偏向锁被撤销,直接升级为了重量级锁。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000010780d182 (fat lock: 0x000000010780d182)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁结束时,可以看到对象还处于重量级锁状态。
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000010780d182 (fat lock: 0x000000010780d182)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
加锁结束时,并休息1s后,JVM进入了SafePoint,而且对锁进行了降级。
[1.565s][info][safepoint] Application time: 1.0050304 seconds
[1.565s][info][safepoint] Entering safepoint region: Cleanup
[1.565s][info][safepoint] Leaving safepoint region
[1.565s][info][safepoint] Total time for which application threads were stopped: 0.0003323 seconds, Stopping threads took: 0.0000159 seconds
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000294425a701 (hash: 0x294425a7; age: 0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)
再次尝试加锁,对象变为轻量级锁
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x000000016ffe6ab0 (thin lock: 0x000000016ffe6ab0)
8 4 (object header: class) 0x00001000
12 4 (object alignment gap)