@TOC
一、锁升级问题
package com.woniuxy.synchronizedupgrade;
import org.openjdk.jol.info.ClassLayout;
class C{
int a=0;
int d=0;
static String b="123";
}
/**
* Auther: mayuhang <br/>
* Date: 2020/7/8:22:21 <br/>
* Description:
*/
public class ObjectUpGrade {
public static void main(String[] args) throws InterruptedException {
//放在延时前,JVM未开启偏向锁,创建的c1对象,会出现一个问题,它会直接从无锁升级为轻量级锁
C c1 = new C();
System.out.println("启动后,c1:无锁" + ClassLayout.parseInstance(c1).toPrintable());
//预留4s,等待开启偏向锁功能 或者使用命令
//vm参数: -XX:+UseBiasedLocking开启偏向锁
//-XX:BiasedLockingStartupDelay=0 关闭偏向锁4秒延迟
Thread.sleep(4001);
synchronized (c1) {
System.out.println("c1第一次加锁后:轻量级锁" + ClassLayout.parseInstance(c1).toPrintable());
}
//4秒后的对象,就可以有偏向锁状态,如果没有加锁,
//则直接是匿名偏向,也就是没有线程竞争锁状态,无线程id信息
C c = new C();
System.out.println("VM开启偏向锁后:c是匿名偏向锁" + ClassLayout.parseInstance(c).toPrintable());
synchronized (c) {
//对c进行加锁后,出现了线程获取c的Monitor,则会修改为偏向锁
System.out.println("c第一次加锁后:偏向锁" + ClassLayout.parseInstance(c).toPrintable());
}
}
打印结果如下:参考下图,判断对象的锁状态
启动后,c1:无锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000`001`锁状态位 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c1第一次加锁后:轻量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 50 f1 c6 02 (01010000 11110001 11000110 00000010) (46592336)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
VM开启偏向锁后:c是匿名偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c第一次加锁后:偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 e8 af 02 (00000101 11101000 10101111 00000010) (45082629)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
根据第三版《深入理解java虚拟机》第三版13.3.
5偏向锁章节最后几段话,原文如下:
看到这里可能会发现一个问题:
当对象进入偏向状态的时候,Mark Word大部分的空间(23个比特)都用于存储持有锁的线程ID了,这部分空间占用了原有存储对 象哈希码的位置,那原来对象的哈希码怎么办呢?
在Java语言里面一个对象如果计算过哈希码,就应该一直保持该值不变(强烈推荐但不强制,因为用户可以重载hashCode()方法按自己的意愿返回哈希码),否则很多依赖对象哈希码的API都可能存在出错风险。而作为绝大多数对象哈希码来源的Object::hashCode()方法,返回的是对象的一致性哈希码(Identity> Hash> Code),这个值是能强制保证不变的,它通过在对象头中存储计算结果来保证第一次计算之后,再次调用该方法取到的哈希码值永远不会再发生改变。因此,当一个对象已经计算过一致性哈希码后,它就再也无法进入偏向锁状态了;而当一个对象当前正处于偏向锁状态,又收到需要计算其一致性哈希码请求时,它的偏向状态会被立即撤销,并且锁会膨胀为重量级锁。
在重量级锁的实现中,对象头指向了重量级锁的位置,代表重量级锁的ObiectMonitor类里有字段可以记录非加锁状态(标志位为“01”)下的Mark
Word,其中自然可以存储原来的哈希码。
偏向锁可以提高带有同步但无竞争的程序性能,但它同样是一个带有效益权衡(TradeOff)性质的优化,也就是说它并非总是对程序运行有利。如果程序中大多数的锁都总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数-XX:-UseBiasedLocking来禁止偏向锁优化反而可以提升性能"。
其中,这句话和测试结果有区别:
当一个对象当前正处于偏向锁状态,又收到需要计算其一致性哈希码请求时,它的偏向状态会被立即撤销,并且锁会膨胀为重量级锁。
测试代码如下:
package com.woniuxy.synchronizedupgrade;
import org.openjdk.jol.info.ClassLayout;
class C{
int a=0;
int d=0;
static String b="123";
}
/**
* Auther: mayuhang <br/>
* Date: 2020/7/8:22:21 <br/>
* Description:
*/
public class ObjectUpGrade {
public static void main(String[] args) throws InterruptedException {
//放在延时前,JVM未开启偏向锁,创建的c1对象,会出现一个问题,它会直接从无锁升级为轻量级锁
C c1 = new C();
System.out.println("启动后,c1:无锁" + ClassLayout.parseInstance(c1).toPrintable());
//预留4s,等待开启偏向锁功能 或者使用命令
//vm参数: -XX:+UseBiasedLocking开启偏向锁 -XX:BiasedLockingStartupDelay=0 关闭偏向锁4秒延迟
Thread.sleep(4001);
synchronized (c1) {
System.out.println("c1第一次加锁后:轻量级锁" + ClassLayout.parseInstance(c1).toPrintable());
}
//4秒后的对象,就可以有偏向锁状态,如果没有加锁,则直接是匿名偏向,也就是没有线程竞争锁状态,无线程id信息
C c = new C();
System.out.println("VM开启偏向锁后:c是匿名偏向锁" + ClassLayout.parseInstance(c).toPrintable());
synchronized (c) {
//对c进行加锁后,出现了线程获取c的Monitor,则会修改为偏向锁
System.out.println("c第一次加锁后:偏向锁" + ClassLayout.parseInstance(c).toPrintable());
}
// 计算HashCode撤销偏向锁
System.out.println(Integer.toHexString(c.hashCode()));
System.out.println("计算hash后的状态:c是无锁状态" + ClassLayout.parseInstance(c).toPrintable());
synchronized (c) {
System.out.println("第二次加锁后:c直接升级为轻量级锁" + ClassLayout.parseInstance(c).toPrintable());
}
new Thread(()->{
synchronized (c) {
System.out.println("还未存在线程竞争:c为轻量级锁" + ClassLayout.parseInstance(c).toPrintable());
}
}).start();
new Thread(()->{
synchronized (c) {
System.out.println("存在线程竞争:c升级为重量级锁" + ClassLayout.parseInstance(c).toPrintable());
}
}).start();
}
}
结果如下:
启动后,c1:无锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c1第一次加锁后:轻量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 f7 7f 02 (11111000 11110111 01111111 00000010) (41940984)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
VM开启偏向锁后:c是匿名偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c第一次加锁后:偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 e8 68 02 (00000101 11101000 01101000 00000010) (40429573)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
64a294a6
计算hash后的状态:c是无锁状态com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 a6 94 a2 (00000001 10100110 10010100 10100010) (-1567316479)
4 4 (object header) 64 00 00 00 (01100100 00000000 00000000 00000000) (100)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
第二次加锁后:c直接升级为轻量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f8 f7 7f 02 (11111000 11110111 01111111 00000010) (41940984)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
还未存在线程竞争:c为轻量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 20 f5 ae 21 (00100000 11110101 10101110 00100001) (565114144)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
存在线程竞争:c升级为重量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 5a 2d b8 1c (01011010 00101101 10111000 00011100) (481832282)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
总结
升级状态,全部都实现了一遍。
从无锁,到匿名偏向锁,到偏向锁,到轻量级锁,到重量级锁。
如果需要根据引用的文章,测试偏向锁计算HashCode后直接升级为重量级锁,代码如下:
package com.woniuxy.synchronizedupgrade;
import org.openjdk.jol.info.ClassLayout;
class C{
int a=0;
int d=0;
static String b="123";
}
/**
* Auther: mayuhang <br/>
* Date: 2020/7/8:22:21 <br/>
* Description:
*/
public class ObjectUpGrade {
public static void main(String[] args) throws InterruptedException {
//放在延时前,JVM未开启偏向锁,创建的c1对象,会出现一个问题,它会直接从无锁升级为轻量级锁
C c1 = new C();
System.out.println("启动后,c1:无锁" + ClassLayout.parseInstance(c1).toPrintable());
//预留4s,等待开启偏向锁功能 或者使用命令
//vm参数: -XX:+UseBiasedLocking开启偏向锁 -XX:BiasedLockingStartupDelay=0 关闭偏向锁4秒延迟
Thread.sleep(4001);
synchronized (c1) {
System.out.println("c1第一次加锁后:轻量级锁" + ClassLayout.parseInstance(c1).toPrintable());
}
//4秒后的对象,就可以有偏向锁状态,如果没有加锁,则直接是匿名偏向,也就是没有线程竞争锁状态,无线程id信息
C c = new C();
System.out.println("VM开启偏向锁后:c是匿名偏向锁" + ClassLayout.parseInstance(c).toPrintable());
synchronized (c) {
//对c进行加锁后,出现了线程获取c的Monitor,则会修改为偏向锁
System.out.println("c第一次加锁后:偏向锁" + ClassLayout.parseInstance(c).toPrintable());
}
synchronized (c) {
c.hashCode();
System.out.println("第二次加锁后:计算HashCode后偏向直升重量级锁:" + ClassLayout.parseInstance(c).toPrintable());
}
}
}
启动后,c1:无锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c1第一次加锁后:轻量级锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 98 f3 bf 02 (10011000 11110011 10111111 00000010) (46134168)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
VM开启偏向锁后:c是匿名偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
c第一次加锁后:偏向锁com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 e8 6b 02 (00000101 11101000 01101011 00000010) (40626181)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
第二次加锁后:计算HashCode后偏向直升重量级锁:com.woniuxy.synchronizedupgrade.C object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 9a 45 ba 1c (10011010 01000101 10111010 00011100) (481969562)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int C.a 0
16 4 int C.d 0
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total