【杂笔】jol分析锁膨胀机制

本文详细解析了JVM中的锁膨胀过程,包括偏向锁的检查、升级和撤销,以及轻量级锁和重量级锁的加解锁流程。通过实例展示了不同情况下的锁状态转换和JVM的自动处理机制。
摘要由CSDN通过智能技术生成

jol分析锁膨胀流程

前置准备

  • 对象头的markword的格式
    在这里插入图片描述

  • 引入依赖

		<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>

JVM对偏向锁进行了优化,4s后才开启。在这里关闭或者手动休眠4s:

public static void main(String[] args) throws Exception {
        //-XX:BiasedLockingStartupDelay=0
        System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
        TimeUnit.SECONDS.sleep(4);
        System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
    }

结果因为涉及到JVM的端序问题,所以应该反着才是真正的存储:
即:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001,第一个是偏向位,二三是标志位
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101,第一个偏向位,二三标志位

在这里插入图片描述
休眠太慢了,这里直接关闭偏向延迟时间,让创建对象的时候直接设置为匿名偏向锁。

-XX:BiasedLockingStartupDelay=0

偏向锁

加锁流程:

  • 最开始无锁状态,一个线程进入同步块,先检查是不是偏向锁【锁标志01,偏向标志1】。
  • 检查偏向线程id==自己
    • 如果是,无需CAS,执行同步块。
    • 如果不是,CAS替换为自己;替换成功执行同步块;替换失败执行锁撤销。

锁撤销流程:

  • 等待全局安全点,暂停持有锁的线程
  • 如果已经退出同步块,将锁设置成无锁状态。
  • 如果没有退出同步块,将锁升级为轻量级锁。
		Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        synchronized (o){//注意关闭逃逸分析,不然JVM会将这个代码优化为没有加锁//
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
        TimeUnit.SECONDS.sleep(1000);
        new Thread(()->{
            synchronized (o){//注意关闭逃逸分析,不然JVM会将这个代码优化为没有加锁//
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

结果:

轻量锁【偏向锁撤销可能会升级轻量锁】

根据锁撤销流程,可能锁会升级成轻量锁;
轻量锁的加解锁流程:

  • 访问代码块前,需要在栈帧创建lock record拷贝一份mw。
  • 获取锁时,需要通过CAS将对象头的mw改为lr的指针,写入成功则锁获取成功;失败则自旋,自旋超过最大次数升级为重量级锁。
  • 释放锁时,通过CAS操作将mw从lr写入对象头,写入成功则锁释放,写入失败则存在竞争【正在释放,一个线程升级重量锁】,膨胀为重量级锁。
static Object a = new Object();

    public static void main(String[] args) throws Exception {


        Object o = new Object();
        System.out.println("匿名偏向锁:"+ClassLayout.parseInstance(o).toPrintable());
        Thread t1 = new Thread(() -> {
            synchronized (o) {
                System.out.println("t1" + ClassLayout.parseInstance(o).toPrintable());
            }
        });
        t1.start();
        t1.join();
        System.out.println("并未主动撤销锁:"+ClassLayout.parseInstance(o).toPrintable());
        Thread t2 = new Thread(() -> {
            synchronized (o) {
                System.out.println("t2:" + ClassLayout.parseInstance(o).toPrintable());
            }
        });
        t2.start();
        t2.join();
        System.out.println("锁撤销完毕:"+ClassLayout.parseInstance(o).toPrintable());

    }

尽管t2执行时,t1已经完成了,t2尝试获取,可能会进行锁撤销,然后获取锁。但是由于撤销的时机由JVM决定,所以该程序有两种结果。
当运行到一个下全局安全点的时候,暂停线程,根据此时线程同步块的执行来决定是升级还是设置为无锁。

在这里插入图片描述

在这里插入图片描述

这里是锁撤销时,升级为轻量级锁,轻量级锁执行完毕,将栈中mk转入对象头。
在这里插入图片描述

重量级锁

 		Object o = new Object();
        new Thread(()->{
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();
        new Thread(()->{
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

因为这里的jol打印对象头已经很耗时了,所以不用自己手动休眠了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值