分析对象内部结构,并详解synchronized锁膨胀升级和降级的过程
1. 对象内部结构
一个对象内部结构由对象头、实例成员以及对齐填充组成。其中对象头由64位的Mark Word以及元数据指针和数组长度(只有数组对象才有)组成,其中元数据指针指向的是元空间中该对象的类.class。其中Mark Word包括一位偏向状态和两位锁状态标志,如下图。
2. 内部结构查看
2.1 jor-core插件下载
首先,我们去maven仓库下载一个jol-core插件或者在pom.xml中配置一下依赖,我这边使用的是IDEA2020版,所以下载了2020年的一个版本,如下图。
2.2 对象内部结构分析
2.2.1 不是数组对象,而且有填充补齐位。填充补齐位为了凑成8个字节,64位,方便64位系统寻址。
2.2.2 不是数组对象,且没有填充补齐位
2.2.3 数组对象
3. 锁升级或降级过程的锁标志位
3.1 偏向锁的启用和延时
3.1.1 偏向锁是否启用,可通过-XX:+UseBiasedLocking开启偏向锁以及-XX:-UseBiasedLocking关闭偏向锁;偏向锁启动延时时间,可通过-XX:BiasedLockingStartupDealy变量设置时间,ms为单位。通过 java -XX:+PrintFlagsFinal -version | findstr "BiasedLocking" 可查看java关于偏向锁的默认设置,如下图。
3.1.2 启动偏向锁,并设定偏向锁启动延迟时间为4s,如下图。
3.2 偏向锁升级、膨胀过程(本节操作都是在开启偏向锁,而且延时为0的场景下)
3.2.1 升级为偏向锁,高位的三位锁标志是101(带线程id);释放锁不会降级。
3.2.2 升级为轻量级锁,高位的锁标志是两位00。如果抢锁的线程和对象头内偏向锁记录的线程不一致时,升级为轻量级锁;
注意,其它博客提到的这么一句话:如果偏向锁的那个偏向线程不存在,锁不升级。经过实践,仍然还是升级为轻量级锁了!!!
3.2.3 再次抢锁时,直接从无锁状态变为轻量级锁,然后释放锁又降级为无锁状态
3.2.4 只要两个线程同时争抢,那么线程直接就升级为重量级锁,不管原先是什么锁状态;而且释放锁后,也不会降级。
这边需要提一下Lock Record,每一个线程在抢锁时,都会先在线程的栈帧中创建存储Lock Record的空间(存储共享对象的Mark Word的拷贝);然后每个线程抢锁时都会尝试将共享对象的Mark Word指向自己的Lock Record。最终,共享对象的Mark Word会存储抢锁成功线程的Lock Record地址;那些抢锁失败的线程会CAS自旋一定次数,如果在指定次数内成功了(默认10次),那么就更改共享对象的Mark Word;如果指定次数后仍不成功,就将锁升级为重量级锁了。
附录
最终出现了一个问题,系统开启偏向锁,两个线程间隔100ms抢锁后都是偏向锁,第二个线程抢锁没有升级为轻量级锁;而且两次对象头内容都一样,为什么???
当两个线程抢锁的时间间隔改为20ms,锁升级变化过程又正常了。
总结
以上是生活随笔为你收集整理的分析对象内部结构,并详解synchronized锁膨胀升级和降级的过程的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Java虚拟机JVM常用的几种回收算法和
- 下一篇: java源码编译为字节码的流程