Java对象内存布局和Synchronized锁升级(二)

news2024/11/24 19:54:48

在这里插入图片描述

目录

  • 对象内存布局
    • 对象头
    • 实例数据
    • 对齐填充
    • 锁在内存布局中的标志位
  • 锁升级
    • 无锁
    • 偏向锁
      • 偏向锁升级
    • 轻量级锁
    • 重量级锁
  • 锁消除和锁粗化
    • 锁消除
    • 锁粗化
  • 锁升级总结

对象内存布局

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
在这里插入图片描述

对象头

对象头里面有两部分内容:对象标记Mark Word和类元信息(类型指针)

对象标记Mark Word存储是什么数据?

  • 对象哈希码、对象分代年龄
  • GC标记
  • GC次数
  • 同步锁标记
  • 偏向锁持有者

类元信息存储的是指向该对象类元数据的首地址

对象头有多大?
在64位系统中,MarkWord占了8个字节,类型指针占了8个字节,一共是16字节

案例说明:
也就是说一个简单的object对象的话,一共是16字节大小
在这里插入图片描述

对象头很重要,后面讲到锁升级和降级的时候,就是锁的标志位,在对象头里演变

实例数据

存放类的属性数据信息,包括父类的属性信息

对齐填充

虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的,仅仅是为了字节对齐这部分内存按8字节补充对齐。

锁在内存布局中的标志位

在这里插入图片描述

锁升级

锁升级背景:
用锁能够实现数据的安全性,但是会带来性能下降,无锁能够基于线程并行提升程序性能,但是会带来安全性下降,那么怎么平衡呢?

基于Java对象内存布局得知,对象头中的Mark Word根据锁标志位的不同可以完成我们的锁升级策略

锁升级过程:无锁->偏向锁->轻量锁->重量级锁

偏向锁:MarkWord存储的是指向偏向的线程ID
轻量锁:MarkWord存储的是指向线程栈中Lock Record的指针
重量级锁:MarkWord存储的是指向堆中的monitor对象的指针
在这里插入图片描述
Java5之前是直接使用Synchronized重量级锁,计算机底层的用户态和内核态之间切换的原理,假如锁的竞争比较激烈的话,性能变低,所以java6之后,为了减少获得锁和释放锁带来的性能消耗,引入了轻量级锁和偏向锁

接下来根据锁的种类和锁演化顺序,给大家来讲解

无锁

初始状态,一个对象被实例化后,如果还没有被任何线程竞争锁,那么它就为无锁状态(001)
在这里插入图片描述

偏向锁

在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用他的线程拥有,这个线程就是锁的偏向线程。

那么只需要在锁第一次被拥有的时候,记录下偏向线程ID。这样偏向线程就一直持有着锁(后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接会去检査锁的MarkWord里面是不是放的自己的线程ID)。

  • 如果相等,表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了,直到竞争发生才释放锁。以后每次同步,检查锁的偏向线程ID与当前线程!D是否一致,如果一致直接进入同步。无需每次加锁解锁都去CAS更新对象头。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外开销,性能极高。

  • 如果不等,表示发生了竞争,锁已经不是总是偏向于同一个线程了,这个时候会尝试使用CAS来替换MarkWord里面的线程ID为新线程的ID

    • 竞争成功,表示之前的线程不存在了, MarkWord里面的线程ID为新线程的ID,锁不会升级,仍然为偏向锁
    • 竞争失败,这时候可能需要升级变为轻量级锁,才能保证线程间公平竞争锁。注意,偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。

编向锁在IDK1.6之后是默认开启的,但是启动时间有延迟,所以需要添加参数-XX:BiasedLockingstartupDelay=0,让其在程序启动时立刻启动

开启编向锁:
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

关闭偏向锁:关闭之后程序默认会直接进入轻量级锁状态。
-XX:-UseBiasedLocking
在这里插入图片描述

偏向锁升级

偏向锁使用一种等到竞争出现才释放锁的机制,只有当其他线程竞争锁时,持有偏向锁的原来线程才会被撤销。撤销需要等待全局安全点(该时间点上没有字节码正在执行),同时检查持有偏向锁的线程是否还在执行

1)第一个线程正在执行synchronized方法(处于同步块),它还没有执行完,其它线程来抢夺,该偏向锁会被取消掉并出现锁升级。
此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程会进入自旋等待获得该轻量级锁。
2)第一个线程执行完成synchronized方法(退出同步块),则将对象头设置成无锁状态并撤销偏向锁,重新偏向。

在这里插入图片描述
但是 java15逐步废弃偏量锁 这个需要注意下

轻量级锁

多线程竞争,但是任意时刻最多只有一个线程竞争,不存在锁竞争太过激烈的情况,也就没有线程阳塞,轻量级锁是为了在线程近乎交替执行同步块时提高性能。

主要目的:在没有多线程竞争的前提下,通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗,说白了先自旋,不行才升级阻塞
升级时机: 当关闭偏向锁功能或多线程竞争偏向锁时会导致偏向锁升级为轻量级锁

假如线程A已经拿到锁,这时线程B又来抢该对象的锁,由于该对象的锁已经被线程A拿到,当前该锁已是偏向锁了。而线程B在争抢时发现对象头Mark Word中的线程ID不是线程B自己的线程ID(而是线程A),那线程B就会进行CAS操作希望能获得锁。此时线程B操作中有两种情况:

  • 如果锁获取成功,直接替换Mark Word中的线程ID为B自己的ID(A - B),重新偏向于其他线程(即将偏向锁交给其他线程,相当于当前线程"被"释放了锁),该锁会保持偏向锁状态,A线程Over,B线程上位;
  • 如果锁获取失败,则偏向锁升级为轻量级锁(设置偏向锁标识为0并设置锁标志位为00),此时轻量级锁由原持有偏向锁的线程持有,继续执行其同步代码,而正在竞争的线程B会进入自旋等待获得该轻量级锁。
    在这里插入图片描述

重量级锁

Java中synchronized的重量级锁,是基于进入和退出Monitor对象实现的。在编译时会将同步块的开始位置插入monitorenter指令,在结束位置插入monitor exit指令。
当线程执行到monitor enter指令时,会尝试获取对象所对应的Monitor所有权,如果获取到了,即获取到了锁,会在Monitor的owner中存放当前线程的id,这样它将处于锁定状态,除非退出同步块,否则其他线程无法获取到这个Monitor。
在这里插入图片描述

锁消除和锁粗化

简单来说,两种错误使用锁的案例,编译不会出问题,但是是无实际意义的代码,可以简单看看

锁消除

从JIT角度看相当于无视它,synchronized(o)不存在了这个锁对象并没有被共用扩散到其它线程使用,极端的说就是根本没有加这个锁对象的底层机器码,消除了锁的使用
在这里插入图片描述
在这里插入图片描述

锁粗化

假如方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器就会把这几个synchronized块合并成一个大块,加粗加大范围,一次申请锁使用即可,避免次次的申请和释放,提升了性能

在这里插入图片描述
上面的代码等同于一个锁里面的执行块
在这里插入图片描述

锁升级总结

Synchronized锁升级过程总结:先自旋,不行再阻塞
实际上是把之前的悲观锁(重量级锁)变成在一定条件下使用偏向锁以及使用轻量级(自旋锁CAS)的形式

Synchronized在修饰方法和代码块在字节码上实现方式有很大差异,但是内部实现还是基于对象头的MarkWord来实现的。JDK1.6之前synchronized使用的是重量级锁,JDK1.6之后进行了优化,拥有了无锁->偏向锁->轻量级锁->重量级锁的升级),而不是无论什么情况都使用重量级锁。

  • 偏向锁:适用于单线程适用的情况,在不存在锁竞争的时候进入同步方法/代码块则使用偏向锁。
  • 轻量级锁:适用于竞争较不激烈的情况(这和乐观锁的使用范围类似),存在竞争时升级为轻量级锁,轻量级锁采用的是自旋锁,如果同步方法/代码块执行时间很短的话,采用轻量级锁虽然会占用cpu资源但是相对比使用重量级锁还是更高效。
  • 重量级锁:适用于竞争激烈的情况,如果同步方法/代码块执行时间很长,那么使用轻量级锁自旋带来的性能消耗就比使用重量级锁更严重,这时候就需要升级为重量级锁。

就先说到这 \color{#008B8B}{ 就先说到这} 就先说到这
在下 A p o l l o \color{#008B8B}{在下Apollo} 在下Apollo
一个爱分享 J a v a 、生活的小人物, \color{#008B8B}{一个爱分享Java、生活的小人物,} 一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞! \color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!} 咱们来日方长,有缘江湖再见,告辞!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1992827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

同态加密和SEAL库的介绍(八)性能

本篇会对比三种加密方案,同时每种方案配置三种参数。即九种情况下的各个操作的性能差异,为大家选择合适的方案和合适的参数提供参考。表格中所有时长的单位均为微妙,即 。 当然数据量比较大,为了方便大家查找&#xff0c…

应用商店故障(UOS系统)

应用商店故障(UOS系统) 1. 安装应用商店内的应用无法下载,更新系统时提示依赖关系被破坏,怎么办? 问题描述 安装应用商店内的应用无法下载,更新系统时均提示依赖关系被破坏 解决方案 1、可先建议用户尝试修…

day 22线程间通信

一、互斥锁 1、资源: 资源是有限的,在程序运行过程中,一段代码、一段空间、一个变量、CPU、内存都可以看做资源 2、互斥锁: 是一种资源,当一个线程任务加锁,其余线程任务无法再次加锁,直到解锁后才能加锁,互斥…

数据机房防静电措施有哪些?安装防静电地板时记住这几点

生活中静电无处不在,一般情况静电不会对我们有什么影响,但在一些特殊场合,比如数据机房、配电室、消控室、电子厂房等,静电的危害必须要引起重视,因为这些场合通常有比较多的电子设备,电子设备中有比较多的…

priority_queue模拟实现【C++】

文章目录 全部的实现代码放在了文章末尾什么是适配器模式?准备工作包含头文件定义命名空间类的成员变量什么是仿函数?比较仿函数在priority_queue中的作用通过传入不同的仿函数可以做到大堆和小堆之间的切换通过传入不同的仿函数可以做到改变priority_qu…

[Leetcode][Medium]-面试题 17.14.最小k个数-TOP K问题-快排/大根堆

一、题目描述 原题地址 二、整体思路 (1)、快排 数组中最小的k个数就是说把数组升序排列,求[0,k-1]区间上的数。 快排可以得到一个元素在升序排序的数组中的正确位置。在这个位置的左边区间[l,l2-1]上的元素都比它小,在这个位置的右边区间[r2,r]上的元素…

zabbix 监控软件

zabbix 监控软件 自带图形化界面,通过网页就可以监控所有的服务器的状态。 事件告警,邮箱通知(噩梦)。 zabbix是什么? web界面提供的分布式监控以及网络监控功能的开源的企业级的软件解决方案。 服务端 监控端 客…

App安装来源追踪的四大方案解析

App的开发者和运营商,都会研究分析渠道的效果,而对渠道来源的追根溯源是一切分析的基础。假如没有明确的安装来源数据,至少会造成以下几种后果: 没有安装来源数据,我们无法判断各个投放渠道流量的价值,也就…

全网首发!鸿蒙OS登上PC,冒风险流出内测,系统界面截图,过会儿就删

开玩笑的啦,其实这是Deepin操作系统的截图,很漂亮吧,这是deepin v23 rc2 的主题之一 鸿蒙还没有发,不知道24年末还能不能上,emmm

Python实战:基础语法

一、求解列表中的最大元素 import random#定义函数 def get_max(lst):x lst[0] #x存储的是元素的最大值#遍历操作for i in range(1,len(lst)):if lst[i] > x:x lst[i] #对最大值进行重新赋值return x#调用函数 lst [random.randint(1,100) for item in range(10)] print…

基于SiliconCloud快速体验GraphRag.Net

SiliconCloud介绍 SiliconCloud 基于优秀的开源基础模型,提供高性价比的 GenAI 服务。 不同于多数大模型云服务平台只提供自家大模型 API,SiliconCloud上架了包括 Qwen、DeepSeek、GLM、Yi、Mistral、LLaMA 3、SDXL、InstantID 在内的多种开源大语言模…

sgetrf M N is 103040 时报错,这是个bug么 lapack and Openblas the same,修复备忘

1,现象 MN103040时,调用 sgetrf_ 时,无论是 LAPACK 还是 OpenBLAS,都出错: openblas: lapack: 2, 复现代码 出现问题的应该是由于M和N相对数字太大,乘积超出32bit整数的表达范围,…

【踩坑】TypeScript 中使用 sass 动态设置样式

问题 在从 Vue2 项目转向 Vue3 项目时,不得不将已经封装好的 echarts 图表也升级成 Vue3 适配的版本,遇到了一个有些诡异的问题,在此记录一下。 背景: 在 Vue2 的项目中,为了动态设置 echarts 的相关配置,于…

trie算法

1、定义 高效的存储和查找字符串集合的数据结构 它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高 2、构建 我们可以使用数组来模拟实现Trie树。 我们设计一个二维数组 son[N] [26] 来…

WhatsApp收不到验证短信的原因及解决方案

在使用WhatsApp进行账号注册或验证过程中,有时会遇到无法收到验证短信的情况。这种情况可能会给用户带来诸多不便,但通常可以通过一些简单的方法来解决。本文将详细分析收不到验证短信的可能原因,并提供相应的解决方案,帮助用户顺…

LeetCode_sql_day15(262.行程与用户)

描述:262. 行程和用户 - 力扣(LeetCode) 取消率 的计算方式如下:(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)。 编写解决方案找出 "2013-10-01" 至 "2013-10-03" 期间非禁止…

EMF矢量图工具Graphpad Prism(棱镜科研绘图工具)

Graphpad Prism 是一款功能强大、专业实用的棱镜科研绘图软件,专为科研工作者而设计研发,可帮助用户进行专业便捷的科研图像绘制,通过该款软件用户可以进行新型子列图进行创建,可以进行平滑的线性图进行绘制,可以说是一…

【汉明距离总和】python刷题记录

R4-数与位篇 class Solution:def totalHammingDistance(self, nums: List[int]) -> int:#创建计数器trieCounter()max_bitlen(bin(max(nums)))-2ret0for i,num in enumerate(nums):for j in range(max_bit):#一位位地取出来bit(num>>j)&1if bit:reti-trie[j]trie[…

同态加密和SEAL库的介绍(一)简介

写在前面: 最近在做同态相关的内容,这里记录下相关的知识点和所踩过的坑,希望对大家有帮助。预计分几篇来详细介绍,从概念简介到不同模式介绍,具体包括了每种模式的编解码和加解密以及他们性能的比对。 虽然同…

MySQL 8.0新特性

文章目录 一. 账户与安全1. 查看用户信息2. 用户权限管理范围3. 用户创建和授权1) 创建并授权用户2)登录zhp,密码zhp.1221。验证数据库权限3)查看用户权限4)撤销用户权限5)用户重命名&修改密码6&#x…