当前位置:网站首页>对象头和synchronized的升级
对象头和synchronized的升级
2022-08-02 14:16:00 【微生鸿朗】
一、线程的历史
二、CAS的原理模型
CAS全拼又叫做compareAndSwap,从名字上的意思就知道是比较交换的意思、它是一条CPU并发原语。它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。
三、对象头的理解
1、对象在内存中的存储情况
注释:Markword 对象头占用8字节 、class pointer 类型指针占用4个字节 、instance data 实例数据、padding 对齐(主要作用是补齐字节数,达到被8整除的状态)。
- 小知识
C:\>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=131697344 -XX:MaxHeapSize=2107157504 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)
解释:在这个指令中,可以打印出的参数的类型
-XX:InitialHeapSize=131697344表示的是起始的堆大小,
-XX:MaxHeapSize=2107157504表示的是最大的堆的大小
-XX:+PrintCommandLineFlags表示的是输入的堆的大小
-XX:+UseCompressedClassPointers表示的是压缩指针,占用4个字节
-XX:+UseCompressedOops表示的是普通对象指针,默认是压缩的4个字节
-XX:+UseCompressedOops
Java的指针的长度是64位,8个字节的,在默认开启了指针压缩后,会压缩成为4个字节
2、实际操作
2.1引入maven依赖
<!-- 引入jol工具的依赖-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.11</version>
</dependency>
</dependencies>
2.2写入测试代码
public class T01 {
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
解释:
2.3执行方法后的结果
Connected to the target VM, address: '127.0.0.1:61099', transport: 'socket'
java.lang.Object 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) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
在图片中可以看到对象头是占用了8个字节,压缩指针占用4个字节,在后面由于不能被8整除,使用字节进行补齐。
四、锁状态的描述
解释:图中的unused表示没有使用,位数对应的值在该状态下使用的位数。
1、无锁状态:25位没有用;31位装的identity Hashcode,但是只有在被调用的时候,才填充,没有调用的时候是空的;1位没有使用的;4位分代年龄;1位偏向锁位;2位锁标志位。
2、偏向锁:54位存下当前线程的ID;2位存批量撤销Epoch;1位没有使用;4位分代年龄;1位偏向锁位;2位锁标志位。
3、自旋锁:62位指向线程中的Lock Record的指针。Lock Record与锁重入有关,synchronize默认是可重入的。自旋锁在竞争锁的时候,会在自己的内存的线程栈中创建一个Lock Record对象,抢到锁对象的资源时,锁对象头存的就是这个线程的Lock Record对象的指针,所以在重入的时候,会再创建一个Lock Record对象,利用Lock Record来记录到底琐了多少次。解锁的时候,就将一个Lock Record移除,移除的方式是FILO,也就是先进后出的原则。
4、重量级琐:重量级琐是在C++代码层面进行的,会生成一个ObjectMonitor对象,这个对象中记录了一系列的队列,
5、 分代年龄:JVM有10种垃圾回收器,前面7种都涉及分代年龄,采用分代算法。当我们创建一个对象的时候,把它放在年轻代中,每经过一次垃圾回收后年龄就+1,也就是垃圾回收无法回收掉这个对象,它的年龄就会不断的增长,到达15,因为4个字节最大为15,就转到老龄代,年轻代的回收就不再对它进行回收。
五、synchronized的升级过程
注意:
(1) 琐升级路线:new -> 偏向锁 -> 轻量级琐(自旋锁、自适应自旋锁) -> 重量级琐
(2) 如果偏向锁没有启动,那么new出来的对象是普通对象;如果偏向锁已经启动,那么new出来的对象就是匿名偏向对象。
(3) 偏向锁什么情况下转为轻量级琐呢?只要有2个线程竞争同一个琐资源,所有线程都升级为轻量级琐,也就是都会自旋抢占琐资源。
(4) 自旋锁在竞争资源的时候,也是CAS操作,用CAS的方式修改琐对象的mackword。
(5) 自旋锁升级为重量级琐需要符合什么条件呢?jdk1.6以前,某个线程自旋次数上限达到默认的10次,会升级为重量级锁,因为一直自旋消耗CPU资源;自旋线程数量达到了默认是系统的CPU核数的1/2的时候,全部升级为重量级琐进入等待队列。jdk1.6之后,jdk提供了自适应自旋,jdk根据每个线程的运行情况来判断是否需要升级。
六、总结
主要通过学习,了解对象头的相关知识和synchronized的升级过程。如有不对的地方敬请指导交流。
边栏推荐
猜你喜欢
随机推荐
优先级表和Ascll表
分布式一致性协议-Paxos
Linux下mysql的彻底卸载
自动化之参数化
Zabbix: PHP option“date.timezone” Fail
Mysql索引优化一
Oauth2.0 自定义响应值以及异常处理
Xrdp 体验优化
Mysql索引底层数据结构
audio console无法连接到RPC服务
HCIE学习记录——数通网络基础
深入理解负载均衡
How to tick the word box?
Oauth2.0 authentication server construction
【软件测试】自动化测试selenium3
Oauth2.0 资源服务器搭建
【软件测试】用例篇
Oauth2.0 resource server construction
WeTest----如何查看Wetest生成测试报告?
【solidity智能合约基础】节约gas的利器--view和pure