11. 嗨, 需要谈个对象…………?

只谈C++11新特性 - std::move std::move是 C++11 中移动语义的重要组成部分。它的实现基于模板和类型特性,通过和将对象转换为右值引用。理解其工作原理和限制对编写高效的 C++ 程序至关重要。 阅读详情

嗨, 空闲时间, 谈个对象…………头?

image

对不起, 咸鱼君又调皮了.

今天, 我要和你谈的不是对象,

image

而是对象头.

没错, 就是Java对象里的对象头知识.

(专业技术文, 阅读需谨慎)

image

前言

上章我们说了synchronized的基本原理, 了解到了synchronized的三种加锁方式

  • 方法锁
  • 类锁
  • 对象锁

抱着往底层深挖的心态, 本章继续深入了解synchronized, 看看在底层, synchronized锁究竟是怎么实现的!

image

为了说清synchronized锁的底层原理,我们得先讲讲两个概念

  • Java对象头

  • 监视器(Monitor)

Java对象头

在JVM中, Java对象在内存中的布局分为三块区域,
Java对象在内存中的布局

  • 对象头

Java对象头一般占有2个机器码
在32位虚拟机中,1个机器码等于4字节, 也就是32bit;
在64位虚拟机中, 1个机器码是8个字节,也就是64bit;

但是如果对象是数组类型, 则需要3个机器码,
因为JVM虚拟机虽然可以通过Java对象的元数据信息确定Java对象的大小,
但是无法从数组类型的元数据来确认数组的大小, 所以需要额外使用一块来记录数组长度

ps: 元数据是指用来描述数据的数据, 通俗一点,就是描述代码间关系,或者代码与其他资源(例如数据库表)之间内在联系的数据.

  • 实例数据

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

  • 对齐填充

由于虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的, 仅仅是为了字节对齐

synchronized用的锁就是存在Java对象头里的.

那么什么是Java对象头呢?

Hotspot虚拟机的对象头主要包括两部分数据:

  • Mark Word (标记字段)

Mark Word用于**存储对象自身的运行时数据,它是实现轻量级锁和偏向锁**的关键.

  • Class Pointer (类型指针)

Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例.

Java对象头具体结构描述如图:

Java对象头结构组成

Mark Word用于存储对象自身的运行时数据. 如:

  • 哈希码(HashCode)
  • GC分代年龄
  • 锁状态标志
  • 线程持有的锁
  • 偏向线程 ID
  • 偏向时间戳
  • …

下图是Java对象头**无锁**状态下Mark Word部分的存储结构(32位虚拟机):
image.png

对象头信息是与对象自身定义的数据无关的**额外存储成本,
考虑到虚拟机的
空间效率,
Mark Word被设计成一个
非固定的数据结构,
以便在
极小的空间内存上存储尽量多的数据,
它会根据对象的状态
复用**自己的存储空间,
也就是说, Mark Word会随着程序的运行发生变化,
可能变化为存储以下4种数据:

Mark Word可能存储4种数据

64位虚拟机下, Mark Word是64bit大小的, 其存储结构如下:
64位Mark Word存储结构

对象头的最后两位存储了**锁的标志位**, 01是初始状态(未加锁) ;
其对象头里存储的是对象本身的哈希码, 随着锁级别的不同, 对象头里会存储不同的内容.

偏向锁存储的是当前占用此对象的线程ID;
而轻量级锁则存储指向线程栈中锁记录的指针;

从这里我们可以看到, “锁”这个东西, 可能是个**锁记录+对象头里的引用指针(判断线程是否拥有锁时, 将线程的锁记录地址和对象头里的指针地址比较);
也可能是
对象头里的线程ID**(判断线程是否拥有锁时, 将线程的ID和对象头里存储的线程ID比较).

HotSpot虚拟机对象头Mark Word

对象头中Mark Word与线程中Lock Record

在线程进入同步代码块的时候,
如果此同步对象没有被锁定, 即它的锁标志位是01,
则虚拟机首先在当前线程的栈中创建我们称之为“锁记录(Lock Record)”的空间, 用于存储锁对象的Mark Word的拷贝,
官方把这个拷贝称为Displaced Mark Word.
整个Mark Word及其拷贝至关重要.

Lock Record是**线程私有**的数据结构,
每一个线程都有一个可用Lock Record列表,同时还有一个全局的可用列表.
每一个被锁住的对象Mark Word都会和一个Lock Record关联(对象头的MarkWord中的Lock Word指向Lock Record的起始地址),
同时Lock Record中有一个Owner字段存放拥有该锁的线程的唯一标识(或者object mark word), 表示该锁被这个线程占用.

如下图所示为Lock Record的内部结构:

Lock Record描述
Owner初始时为NULL, 表示当前没有任何线程拥有该monitor record; 当线程成功拥有该锁后保存线程唯一标识;当锁被释放时又设置为NULL;
EntryQ关联一个系统互斥锁(semaphore), 阻塞所有试图锁住monitor record失败的线程;
RcThis表示blocked或waiting在该monitor record上的所有线程的个数;
Nest用来实现 重入锁的计数;
HashCode保存从对象头拷贝过来的HashCode值(可能还包含GC age).
Candidate用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁;如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降.Candidate只有两种可能的值0表示;

监视器(Monitor)

任何一个对象都有一个Monitor与之关联,
当且一个Monitor被持有后, 它将处于锁定状态.
synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步,
虽然具体实现细节不一样,
但是都可以通过成对的MonitorEnter和MonitorExit指令来实现.

  • MonitorEnter指令

插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁

  • MonitorExit指令

插入在方法结束处和异常处,JVM保证每个-MonitorEnter必须有对应的MonitorExit

那什么是Monitor?

可以把它理解为 一个同步工具,也可以描述为 一种同步机制,它通常被 描述为一个对象.

在Java的设计中,每一个Java对象自带了一把看不见的锁,
它叫做内部锁或者Monitor锁.

也就是通常说Synchronized的对象锁,MarkWord锁标识位为10,
其中指针指向的是Monitor对象的起始地址.
在Java虚拟机(HotSpot)中, Monitor是由ObjectMonitor实现的,
其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的):

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; // 记录个数
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; // 处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; // 处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中有两个队列.

  • _WaitSet

  • _EntryList

用来保存ObjectWaiter对象列表(每个等待锁的线程都会被封装成ObjectWaiter对象),

_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时:

  1. 首先会进入 _EntryList 集合,当线程获取到对象的monitor后,进入 _Owner区域并把monitor中的owner变量设置为当前线程,同时monitor中的计数器count加1;

  2. 若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒;

  3. 若当前线程执行完毕,也将释放monitor(锁)并复位count的值,以便其他线程进入获取monitor(锁);

同时, Monitor对象存在于每个Java对象的对象头Mark Word中(存储的指针的指向),
Synchronized锁便是通过这种方式获取锁的,
这也是Java中任意对象可以作为锁的原因,
同时notify/notifyAll/wait等方法会使用到Monitor锁对象,
所以必须在同步代码块中使用.

监视器Monitor有两种同步方式:

  • 互斥
  • 协作

多线程环境下线程之间如果需要共享数据,需要解决互斥访问数据的问题,
监视器可以确保监视器上的数据在同一时刻只会有一个线程在访问.

什么时候需要协作?

举个例子

一个线程向缓冲区写数据, 另一个线程从缓冲区读数据.
如果读线程发现缓冲区为空就会等待,
当写线程向缓冲区写入数据,就会唤醒读线程;
这里读线程和写线程就是一个合作关系.
JVM通过Object类的wait方法来使自己等待,
在调用wait方法后,
该线程会释放它持有的监视器, 直到其他线程通知它才有执行的机会.

一个线程调用notify方法通知在等待的线程,
这个等待的线程并不会马上执行,
而是要通知线程释放监视器后,它重新获取监视器才有执行的机会.
如果刚好唤醒的这个线程需要的监视器被其他线程抢占,
那么这个线程会继续等待.
Object类中的notifyAll方法可以解决这个问题,
它可以唤醒所有等待的线程, 总有一个线程执行.

image.png

如图所示,
一个线程通过1号门进入Entry Set(入口区),
如果在入口区没有线程等待,
那么这个线程就会获取监视器成为监视器的Owner,然后执行监视区域的代码;
如果在入口区中有其它线程在等待,
那么新来的线程也会和这些线程一起等待;
线程在持有监视器的过程中,
有两个选择:

  • 一个是正常执行监视器区域的代码, 释放监视器,通过5号门退出监视器;

  • 还有可能等待某个条件的出现,于是它会通过3号门到Wait Set(等待区)休息, 直到相应的条件满足后再通过4号门进入重新获取监视器再执行;

注意

当一个线程释放监视器时,
在入口区和等待区的等待线程都会去竞争监视器;
如果入口区的线程赢了,会从2号门进入;
如果等待区的线程赢了会从4号门进入;
只有通过3号门才能进入等待区,
在等待区中的线程只有通过4号门才能退出等待区;
也就是说一个线程只有在持有监视器时才能执行wait操作,
处于等待的线程只有再次获得监视器才能退出等待状态.

Bala, Bala,……

希望对各位有所帮助.

如果没有, 请在多读几遍~

若是点个赞, 也是极好的~~

image

参考文章: 源码架构

请关注我的订阅号

订阅号.png

DirectX11 采样状态对象 采样状态对象1. 采用状态对象有什么用? 采样状态描述用于描述纹理贴图的采样方式。一个采样状态对象允许我们访问一个纹理的采样状态信息。后面我们看采样状态描述结构体就知道有哪些内容可以描述了。 2. 采样状态可以描述哪些内容? 我们来看采样状态描述结构体D3D11_SAMPLER_DESC有哪些成员: Filter,它指定被采样的纹理怎样过滤。纹理过滤描述从源数据怎样被读取和联 阅读详情

相关推荐

PKCS11:查找公钥对象

介绍在PKCS11中如何查找公钥对象。

密码开发者 3378

深入理解多线程(三)— Java的对象头

深入理解多线程(三)— Java的对象头 在Java对象模型文章中我们以HotSpot虚拟机来简单的介绍了Java的对象模型。本文我们就基于上一篇文章来简单的介绍Java的对象头。主要介绍一下对象头的作用,结构以及他和锁的关系。 1. 概述 每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Jav...

渔潭的博客 926

如何使用 YOLOv11 分割对象

如今,随着 YOLOv11 的推出,我们看到了性能和功能的显著提升,尤其是在物体分割方面——这项关键技术不仅使我们能够检测物体,还能在图像中区分物体的精确边界。与单纯的检测不同,物体分割旨在理解物体的精确空间属性,从而将图像划分成有意义的区域。YOLOv11 采用先进的卷积层和创新的主干网络,即使在复杂或杂乱的场景中也能提供更清晰、更准确的物体边界。在本篇博文中,我将向您展示 YOLOv11 如何改进分割任务,并概述如何在您自己的项目中实现它的步骤。上,你可以比较不同的模型,并权衡它们各自的优缺点。

2501_91623519的博客 1341

再谈类与对象

再谈类与对象再谈构造函数初始化列表二级目录三级目录 再谈构造函数 初始化列表 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 虽然构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。 初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。 class Date { pub

AI_ELF的博客 799

C++11 lambda表达式与函数对象

Lambda表达式 、为何使用lambda 、函数对象

ZY-JIMMY 1565

2025反爬终极方案:Python破解Cloudflare v2行为指纹,设备伪装+轨迹模拟拦截率压至1%

Cloudflare v2的反爬逻辑越来越接近“人类行为识别”,单纯的技术破解已失效,核心竞争力变成“模拟真实用户的能力”。本文的终极方案本质是“全方位复刻人类行为”——从设备指纹到操作轨迹,从网络特征到交互模式,让爬虫成为“无法被区分的真实用户”。这套方案的优势是“通用性强”,不仅适用于Cloudflare v2,也能破解大多数高反爬网站(如小红书、知乎、跨境电商)的行为检测;且代码可直接复用,新手只需替换目标URL和数据提取逻辑,就能快速落地。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 2353

C++11:可调用对象

可以调用对象是C++11引入的新概念,指代此对象可以像函数调用方式的触发调用的对象。在C++98标准中,可以被通过函数方式调用的对象有3个,他们分别是普通函数,函数指针,仿函数。C++11中增加了bind生成对象,lamdba表达式和function对象。所以C++11可调用对象总计6种:他们分别是普通函数,函数指针,(仿函数)函数对象,bind生成对象,lamdba表达式和function对象。

一鸣惊人的博客 2574

问题解决:打开VMware创建的虚拟机,电脑立刻蓝屏并重启

VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credenti

weixin_55118477的博客 3万+

c++11总结16——再谈initialize_list

1. 概念 initializer_list是C++11提供的新类型。主要用于列表初始化。 待续。。。

www_dong的博客 3264

npu-smi info 华为昇腾NPU 状态监控工具解读

用于查询所有设备的基本信息。

Lixora's DB Home 2484

AS5600磁编码器IIC通信与角度读取实战指南

本文提供了AS5600磁编码器通过IIC通信协议实现高精度角度读取的完整实战指南。详细解析了硬件连接要点、IIC通信时序、Arduino与STM32平台驱动代码,并分享了多圈测量、噪声滤波等进阶应用与避坑技巧,帮助开发者快速将这款非接触式角度传感器集成到电机控制、旋钮等项目中。

weixin_29172201的博客 242

c++11:可调用对象

std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。使用仿函数,我们可以将其作为参数传递给函数,算法或容器,并在需要的时候 通过运算符来执行相应的操作,它们可以存储状态,实现自定义行为并具有更高的灵活性和可定制性。c++11可调用对象(普通函数,函数指针,仿函数,bind生成对象,lambda表达式,function对象)c++98可调用对象(普通函数,函数指针,仿函数)

燃犀的博客 1503

告别regsvr32!易语言调用大漠插件dm.dll的免注册实战(附Win7/10/11避坑指南)

本文详细介绍了易语言如何免注册调用大漠插件dm.dll的实战方法,通过`dmreg.dll`实现真正的免注册调用,规避了传统`regsvr32`注册方式带来的权限和兼容性问题。文章包含核心原理剖析、易语言实现细节、多系统兼容性方案及高级应用场景,帮助开发者实现绿色化部署,提升自动化脚本开发效率。

weixin_42553471的博客 296

c++11特性——lambda对象、包装器

本文介绍了C++11中引入的lambda表达式和包装器,重点探讨了lambda表达式的语法、使用及其捕捉参数列表的规则。lambda表达式本质上是一个匿名函数对象,可以在函数内部定义,并且可以通过捕捉参数列表访问外部变量。文章详细讲解了lambda表达式的语法结构,包括捕捉列表、参数列表和返回值的省略规则,并通过代码示例展示了lambda对象的使用方法。此外,文章还讨论了lambda对象在使用过程中可能遇到的问题,如无法使用外部非全局变量和递归调用等,并介绍了如何通过捕捉参数列表解决这些问题。最后,文章通过

2301_79705195的博客 1492

骑砍2霸主MOD开发(9)-游戏大地图(MapScene)

Settlement类型由town,castle,castle_village,village,hideout五种类型组成,通过对GameEntity中添加tag实现类型的加载.大地图中军团(MobileParty)的移动速度,AI寻路,食物消耗等均和当前MobileParty所处地形有关,地形统一被定义为TerrainType,类型有森林,荒漠等.MapScene.Read("Main_map", ref sceneInitializationData, "") 装载Main_map大地图。

骑砍战团&霸主MOD开发 1621

17、激光切割焦点位置和入射角对切割质量的影响研究

本研究通过响应面方法(RSM)结合Box-Behnken设计,系统分析了激光功率、切割速度和气体压力对SS 304不锈钢激光切割质量的影响,重点探讨了三种不同喷嘴在不同聚焦条件下的切缝比和条纹深度变化规律。实验结果表明,激光功率、切割速度和气体压力对切割质量具有显著影响,且不同喷嘴因焦点位置差异表现出不同的响应趋势。通过回归建模与优化,确定了最小激光功率、适中切割速度和最大气体压力为最优参数组合,可实现最小切缝比和较优条纹深度。研究为实际激光切割工艺提供了科学的参数选择依据和操作流程建议,有效提升了切割精度

mm9012的博客 124

C++11对象池

构想: 基于C++11封装一个简易的对象池,输出使用 std::vector 存储对象成员,对外提供 std::uniqued_ptr 避免忘记销毁,std::uniqued_ptr释放是自动放回std::vector的尾部。提供最少的接口:add,用于添加一个成员;get,用于获取一个成员;...

chuixianghai2403的博客 437

骚戴笔记公告

骚戴笔记公告

我从不打没有准备的仗! 2万+

C++ 面向对象编程(下)兼谈对象模型

如果输出201103,则表示是C++11。

qq_36151379的博客 934

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++11并发与多线程笔记(11) std::atomic续谈、std::async深入谈

第十一节 std::atomic续谈、std::async深入谈 一、std::atomic续谈 #include <iostream> #include <thread> #include <atomic> using namespace std; std::atomic<int> g_count = 0; //封装了一个类型为int的 对象(值) void mythread1() { for (int i = 0; i < 1000000;

qq_38231713的博客 2697

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

C++11系列——函数对象(Function Object)

之前总结过一篇Boost之高阶函数——函数对象,介绍了几个用于处理函数对象的 Boost C++ 库。而目前C++11的标准库std已经提供了函数对象的一些功能。根据wiki可以知道Higher order funciton的定义如下: In mathematics and computer science, a higher-order function (also functional, f

delphiwcdj的专栏 2325

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

EL11个内置对象

EL一共11个内置对象,无需创建即可以使用。这11个内置对象中有10个是Map类型的,最后一个是pageContext对象。  pageScope  requestScope  sessionScope  applicationScope  param;  paramValues;  header;  headerValues;  initParam;  cookie;  p...

恶魔的眼泪的博客 434

使用C++11实现对象池

使用C++11实现对象池

知其然而后知其所以然 887

对象的消息总线——C++11

对象的消息总线——C++11 学习《深入应用C++11 代码优化与工程级应用》 什么是消息总线 对象之间的关系一般有:依赖、关联、聚合、组合和继承。 在大规模的软件开发过程中,对象很多,关联关系非常复杂,如果没有统一的、简洁的方法去管理这些对象关系,会导致对象关系复杂,后期维护困难。 基于消息总线技术可以解决这些问题。对象之间只通过消息联系,不是通过直接的依赖或者关联。在消息总线中,对象是通过消息来联系,消息即是对象的关系,只需要在消息总线中管理这些消息,不用关心具体哪些对象之间有关联,便于统一

Skynet的博客 716

PKCS#11对象介绍

PKCS#11(又称 Cryptoki)是一套跨平台的 C 语言加密令牌接口标准,规定了访问/使用HSM、智能卡等硬件加密设备,实现密钥管理与加解密、签名验签等密码操作的统一接口,由 OASIS 管理,当前最新版本为 3.1。

愚公 906
上一篇: 41. 从零开始学springboot整合ElasticSearch
下一篇: 12. 面试官不问, 你也不得不知道的锁知识!
码哥说
博客等级 码龄8年 5645粉丝 172原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码哥说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值