JUC-05 详解Synchronized part3

本文详细介绍了Java中Synchronized的底层实现,特别是对象的存储结构和对象头的作用。对象头包括MarkWord和ClassPointer,其中MarkWord是实现锁机制的关键。文章讨论了对象头中的MarkWord如何存储对象哈希码、锁状态等信息,并阐述了锁的四种状态:无锁->偏向锁->轻量级锁->重量级锁。此外,还提及了不同锁状态的优缺点及其适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇文章中我们讲了Synchronized关键字的底层实现第一部分,主要涉及JVM的指令和Monitor对象。今天我们就继续来挖掘一下 Synchronized的底层实现,围绕对象的存储结构和对象头的构成和作用进行一下解析。

本文主要围绕下面两个问题进行解析。

  1. 对象的存储结构了解吗?
  2. 对象头里有什么?作用呢?

1. JVM中对象的存储结构

我们以Hotspot为例,下图描述了Java对象实例在JVM中的存储结构。

对象头:

    1. Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit)。
    2. 特殊情况,对象是数组类型,额外需要4个字节(会多占一个机器码),用来记录数组长度(因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小)。

Hotspot虚拟机的对象头(非数组情况下)主要包括两部分数据:

  • Mark Word(标记字段)
  • Class Pointer(类型指针)

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

Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,64位时8字节,开启指针压缩或者最大堆内存小于32G时为4个字节。

对象头中的MarkWord:

JVM在1.6版本以上默认开启指针压缩,所以会压缩到4个字节(原来8个字节),32bit。

markWord中储存的内容:

      • 对象哈希码、对象分代年龄
      • 指向锁记录的指针
      • 指向重量级锁的指针
      • 空,不需要记录信息
      • 偏向线程 ID、偏向时间截、对象分代年龄

我们这里简单提一下锁的四种不可逆的状态:无锁状态 -> 偏向锁状态 -> 轻量级锁状态 -> 重量级锁状态

下图是64位虚拟机在不压缩情况下的展示,也就是25bit扩充为57bit

instance data 实例数据:

在对象实例的存储结构中,对象头之后的,就是用来存放类的属性数据信息的instance data,其中包括父类的属性信息等内容。

padding 对齐填充:

存储结构中最后部分的对齐填充,主要由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐;(比如:7个Long)

四种不可逆的状态:无锁状态 -> 偏向锁状态 -> 轻量级锁状态 -> 重量级锁状态

下表来自《Java并发编程的艺术》:

优点

缺点

适用场景

偏向锁

加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。

如果线程间存在锁竞争,会带来额外的锁撤销的消耗。

适用于只有一个线程访问同步块场景。

轻量级锁

竞争的线程不会阻塞,提高了程序的响应速度。

如果始终得不到锁竞争的线程使用自旋会消耗CPU。

追求响应时间。同步块执行速度非常快。

重量级锁

线程竞争不使用自旋,不会消耗CPU。

线程阻塞,响应时间缓慢。

追求吞吐量。同步块执行时间较长。

更多更精彩的内容请关注。

        V信公众号搜索   “程序员一棵树”。 内有相关文档原文和免费资料。
        D音搜索             “程序员一棵树”。
        B站搜索             “程序员一棵树”。

### JUC-II中变址寻址的微指令实现细节 #### 1. 变址寻址的基本概念 变址寻址是一种通过基址寄存器与偏移量相加生成有效地址的寻址方式。其核心思想是将指令中的偏移量字段与指定的基址寄存器内容相加,从而计算出目标地址[^1]。在JUC-II模型机中,这种寻址方式常用于数组访问和间接跳转等场景。 #### 2. 微指令的设计原则 微指令是控制计算机硬件执行具体操作的最小单位。在JUC-II模型机中,微指令需要明确描述每一步的操作,包括数据路径的选择、算术逻辑单元(ALU)的操作以及存储器的访问等[^2]。对于变址寻址,微指令设计需满足以下要求: - 提取指令中的偏移量。 - 获取基址寄存器的内容。 - 执行加法操作以生成有效地址。 - 使用有效地址访问内存或寄存器。 #### 3. 微指令的具体实现 以下是针对JUC-II模型机中变址寻址的微指令实现细节: ##### (1) 提取偏移量 从当前指令中提取偏移量字段,并将其存储到临时寄存器中。假设偏移量位于指令的低16位,则微指令可以表示为: ```plaintext MicroOp1: TempReg ← Instruction[15:0] ``` ##### (2) 获取基址寄存器内容 读取指定基址寄存器的内容,并将其加载到另一个临时寄存器中。例如,如果基址寄存器为`Rb`,则微指令为: ```plaintext MicroOp2: BaseRegContent ← Rb ``` ##### (3) 计算有效地址 将偏移量与基址寄存器内容相加,生成有效地址。此操作通常由ALU完成: ```plaintext MicroOp3: EffectiveAddress ← BaseRegContent + TempReg ``` ##### (4) 访问目标地址 使用计算出的有效地址访问内存或寄存器。例如,如果目标是内存单元,则微指令为: ```plaintext MicroOp4: Data ← Memory[EffectiveAddress] ``` #### 4. 微指令的编码形式 微指令的编码形式决定了其在硬件中的实现方式。在JUC-II模型机中,微指令通常采用水平型或垂直型编码[^2]。以下是水平型微指令的一个示例: ```plaintext | 控制信号 | 数据路径选择 | ALU操作 | 存储器访问 | |----------|--------------|---------|------------| | 1 | Src1: BaseRegContent, Src2: TempReg | Add | MemRead | ``` 在此示例中,控制信号指定了ALU执行加法操作,并且选择了`BaseRegContent`和`TempReg`作为输入源。同时,还启用了存储器读操作。 #### 5. 实现中的注意事项 - 确保基址寄存器和偏移量的范围符合硬件限制。 - 如果有效地址超出内存范围,应触发异常处理机制。 - 微指令设计需与硬件结构紧密配合,确保每一步操作都能正确映射到具体的控制信号。 #### 示例代码 以下是一个基于JUC-II模型机的变址寻址微程序伪代码示例: ```plaintext # 提取偏移量 MicroOp1: TempReg ← Instruction[15:0] # 获取基址寄存器内容 MicroOp2: BaseRegContent ← Rb # 计算有效地址 MicroOp3: EffectiveAddress ← BaseRegContent + TempReg # 访问目标地址 MicroOp4: Data ← Memory[EffectiveAddress] ``` #### 6. 相关理论支持 通过设计控制器的微程序,可以实现JUC-II模型机的指令系统,并加深对计算机结构和工作原理的理解。变址寻址作为其中一种重要的寻址方式,其微程序设计对于理解指令执行过程具有重要意义。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员一棵树

创作不易,感谢土豪打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值