深入理解Java内存模型(一)

fastbin attack快速入门 0x01 fastbin简介 Fastbin包含0x20~0x80大小bin的数组指针,用于快速分配小堆块。fastbin按照单链表结构进行组织,相同大小的bin在同一个链表中,fd指向下一个bin,采用LIFO(Last In First Out)机制。在fastbin中,堆块的inuse标志都为1,处于占用状态,防止chunk释放时进行合并,加快小堆块的分配。 正常fastbin的使用 通过这个简单的案例来了解fastbin的单链表结构,malloc和free的过程。 #include<stdio 阅读详情

在java中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享(本文使用“共享变量”这个术语代指实例域,静态域和数组元素)。局部变量(Local variables),方法定义参数(java语言规范称之为formal method parameters)和异常处理器参数(exception handler parameters)不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。

Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。Java内存模型的抽象示意图如下:

从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

  1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
  2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

下面通过示意图来说明这两个步骤:

如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。

从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

重排序

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

  1. 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  2. 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  3. 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

上述的1属于编译器重排序,2和3属于处理器重排序。这些重排序都可能会导致多线程程序出现内存可见性问题。对于编译器,JMM的编译器重排序规则会禁止特定类型的编译器重排序(不是所有的编译器重排序都要禁止)。对于处理器重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。

JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

处理器重排序与内存屏障指令

现代的处理器使用写缓冲区来临时保存向内存写入的数据。写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,可以减少对内存总线的占用。虽然写缓冲区有这么多好处,但每个处理器上的写缓冲区,仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致!为了具体说明,请看下面示例:

Processor AProcessor B
a = 1; //A1
x = b; //A2
b = 2; //B1
y = a; //B2
初始状态:a = b = 0
处理器允许执行后得到结果:x = y = 0

假设处理器A和处理器B按程序的顺序并行执行内存访问,最终却可能得到x = y = 0的结果。具体的原因如下图所示:

这里处理器A和处理器B可以同时把共享变量写入自己的写缓冲区(A1,B1),然后从内存中读取另一个共享变量(A2,B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3,B3)。当以这种时序执行时,程序就可以得到x = y = 0的结果。

从内存操作实际发生的顺序来看,直到处理器A执行A3来刷新自己的写缓存区,写操作A1才算真正执行了。虽然处理器A执行内存操作的顺序为:A1->A2,但内存操作实际发生的顺序却是:A2->A1。此时,处理器A的内存操作顺序被重排序了(处理器B的情况和处理器A一样,这里就不赘述了)。

这里的关键是,由于写缓冲区仅对自己的处理器可见,它会导致处理器执行内存操作的顺序可能会与内存实际的操作执行顺序不一致。由于现代的处理器都会使用写缓冲区,因此现代的处理器都会允许对写-读操做重排序。

下面是常见处理器允许的重排序类型的列表:

 Load-LoadLoad-StoreStore-StoreStore-Load数据依赖
sparc-TSONNNYN
x86NNNYN
ia64YYYYN
PowerPCYYYYN

上表单元格中的“N”表示处理器不允许两个操作重排序,“Y”表示允许重排序。

从上表我们可以看出:常见的处理器都允许Store-Load重排序;常见的处理器都不允许对存在数据依赖的操作做重排序。sparc-TSO和x86拥有相对较强的处理器内存模型,它们仅允许对写-读操作做重排序(因为它们都使用了写缓冲区)。

※注1:sparc-TSO是指以TSO(Total Store Order)内存模型运行时,sparc处理器的特性。

※注2:上表中的x86包括x64及AMD64。

※注3:由于ARM处理器的内存模型与PowerPC处理器的内存模型非常类似,本文将忽略它。

※注4:数据依赖性后文会专门说明。

为了保证内存可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为下列四类:

屏障类型指令示例说明
LoadLoad BarriersLoad1; LoadLoad; Load2确保Load1数据的装载,之前于Load2及所有后续装载指令的装载。
StoreStore BarriersStore1; StoreStore; Store2确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的存储。
LoadStore BarriersLoad1; LoadStore; Store2确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存。
StoreLoad BarriersStore1; StoreLoad; Load2确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)。

happens-before

从JDK5开始,java使用新的JSR -133内存模型(本文除非特别说明,针对的都是JSR- 133内存模型)。JSR-133提出了happens-before的概念,通过这个概念来阐述操作之间的内存可见性。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。 与程序员密切相关的happens-before规则如下:

  • 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
  • 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。
  • 传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。

注意,两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前(the first is visible to and ordered before the second)。happens- before的定义很微妙,后文会具体说明happens-before为什么要这么定义。

happens-before与JMM的关系如下图所示:

如上图所示,一个happens-before规则通常对应于多个编译器重排序规则和处理器重排序规则。对于java程序员来说,happens-before规则简单易懂,它避免程序员为了理解JMM提供的内存可见性保证而去学习复杂的重排序规则以及这些规则的具体实现。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文 答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案 立即下载

相关推荐

Win10家庭版没有Hyper-V?教你一招轻松解决(含避坑指南)

本文详细介绍了在Win10家庭版中启用Hyper-V的完整实战指南。通过DISM工具链绕过系统版本限制,无需重装系统即可完整启用所有Hyper-V组件,适用于开发者测试多系统环境和安全隔离应用场景。文章包含详细操作步骤、常见问题解决方案及性能优化配置,帮助用户轻松解决Win10家庭版没有Hyper-V的问题。

weixin_29239661的博客 495

【JDK】JCP, JEP, JLS, JSR介绍

JCPJava社区标准过程1998JCPJavaJCPJavaJCPJSR。

Java技术栈,分享不断学习、不断超越、不断积累的历程! 1361

system_stm32f10x.c文件分析

配置系统时钟(包括系统时钟源、锁相环(PLL)倍频系数、高级高性能总线(AHB)/ 高级外设总线(APBx)预分频器以及闪存相关设置)。该函数在系统复位后的启动阶段被调用,且调用时机早于跳转到主程序之前。

Jia shuheng的博客 603

Java规范请求(JSR)、Java语言规范(jls)与Java虚拟机规范(jvms)

JSR Java语言规范 Java虚拟机规范

tobrainto 1163

把STM32MP135当单片机玩:实战SD卡脱机运行LED程序(基于MP135DK开发板)

本文详细介绍了如何在STM32MP135开发板上实现SD卡脱机运行LED程序,通过裸机开发方式(Bare Metal)利用HAL库完成GPIO控制,并解析了SD卡启动流程。内容涵盖工程创建、代码编写、SD卡烧录及常见问题排查,为开发者提供了一种高性能单片机开发方案。

weixin_33734785的博客 242

KEPServerEX6教程:IoT Gateway与MQTT Client实现工业数据双向通信

工业物联网的核心挑战在于异构设备协议与云端平台之间的数据互通。Modbus、S7、OPC UA等现场协议纷繁复杂,而上层应用普遍偏好MQTT、REST等轻量接口,此时需要一个可靠的协议转换层。MQTT作为低带宽、高可靠的发布/订阅消息协议,天然适合工业数据采集与远程控制场景。KEPServerEX6作为工业网关软件,内置IoT Gateway与MQTT Client组件,分别承担数据上行发布与指令下行订阅的职责,形成完整的双向闭环。通过配置Modbus TCP设备驱动、创建Tag、设置MQTT Broker

weixin_33787529的博客 387

Java中JCP, JEP, JLS, JSR是什么

Java中常见的JCP, JEP, JLS, JSR代表什么

7me 4600

f103vet6 SD卡SDIO+DMA配置避坑指南:从初始化卡死到稳定读写的全流程解析

本文详细解析STM32F103VET6芯片SD卡SDIO与DMA配置中的常见问题,从初始化卡死到读写不稳定的全流程解决方案。重点分析总线宽度配置、时钟分频调整及DMA通道管理技巧,帮助开发者避开常见陷阱,实现稳定高效的数据存储。

u0v1w2x3的博客 666

你知道Java中的JCP, JEP, JLS, JSR是什么意思吗?

使用JSR圈定标准范围以后就可以提供给JDK团队成员进行开发, 圈定的范围通常是JAVA相关的, 同时又不会成为Java核心技术(Java SE/EE Core)的一部分.,一个标准的JSR课题通常是一个相对成熟的技术。Java Language Specification的缩写,意为Java语法规范,于指出Java的语法标准和一些规则, 这些规则包括了合规及不合规程序的说明. 规范同时指出了程序的含义并说明了运行后将发生什么。JEP用于探索新的一些想法,一般比JSR更为早期,用于前期的探索,是用于收集。

2877

Wireshark、Fiddler、Charles 三款抓包工具的本质差异与选型指南

抓包工具是网络调试与接口分析的基础能力,其核心在于理解OSI模型分层原理——不同工具工作在不同协议层级,解决不同维度问题。Wireshark面向网络层(L2/L3),可捕获原始数据帧与TCP握手细节,适用于DNS异常、丢包重传等底层问题定位;Fiddler和Charles则工作在应用层(L7),作为HTTP(S)代理实现HTTPS解密、请求改写与弱网模拟,是API调试与移动端测试的首选。技术价值体现在:精准匹配问题域(如证书固定需Fiddler脚本绕过,iOS调试依赖Charles证书信任链),避免‘用显微

weixin_28831341的博客 253

MZI and MI Optical Waveguide(马赫曾德与迈克尔逊光波导)

注意,这里的 “镜子” 并不是真的在波导中做一个镜子出来,而是通过使用一个 1x2 的 MMI 结构将 MMI 输出的两个端口连接起来形成一个 “宽带镜子”。对于这里形成镜子的详细原理,后面有时间了我们再做更加详细地介绍。,它由一个 splitter 构成和两个仿佛带有 “镜子” 的臂组成。相比较于 MZI,光波可以在它的臂中传输,最终从进光口处出去得到相长或者相消干涉,很明显,在。)的折射率,从而使得两个臂上产生的相位差值不同,最终得到相长或者相消干涉的结果。这里,给大家介绍一下。,它更像是一个折叠的。

u011699626的博客 2454

JMM(Java内存模型)详解

1.什么是JMM? ​ JMM 是Java内存模型( Java Memory Model),简称JMM。它本身只是一个抽象的概念,并不真实存在,它描述的是一种规则或规范。通过这组规范,定义了程序中对各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。 ​ 计算机在执行程序时,每条指令都是在CPU中执行的。而执行指令的过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过

LYQ20010417的博客 3万+

html2canvas - 解决截图后引入的字体样式丢失问题,第三方字体截图导出图片后字体失效,html2canvas截屏生成图片后艺术字体就丢失并且自动转成常规字体了,特殊字体显示不了无法截图怎么办

vue2,vue3,uniapp,html2canvas截图后引入的字体样式丢失,html2canvas艺术字截图不显示,使用外部字体无效,html2canvas无法识别网络字体文件怎么办,ttf文件,网页使用在线字体链接利用html2canvas无法截取,字体样式没有了,font-family第三方字体,@font-face引入本地字体文件后无法被html2canvas识别怎么办,html2canvas文字消失,vue页面正常显示艺术字体但截屏后没有,html2canvas截图存在特殊字体时截图不全,默认

✅ 精选推荐文章 1万+

深入理解Java内存模型(一)——基础

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过...

天蓝色的博客 187

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

深入理解JAVA虚拟机学习笔记(一)JVM内存模型

 上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了。这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状态和激情都要慢慢褪去了,总觉得心里慌的一逼,这怎么行呢?!趁明天周末,在公司电脑上记录下这周的一些学习内容。近期在看一本很经典的java书籍:《深入理解java虚拟机 第二版》,几年前也翻过,但那时候功力不够,...

sweetfire的博客 302

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

深入理解 Java 内存模型(一)——基础

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写 - 读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。 同步是指程序用于控制不同线程之间操作发生相对顺序的机制。在共享内存并发模

关注我!带你一路 "狂飙" 到底! 171

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

深入理解java虚拟机(一)JVM内存模型

文章目录前言一、JVM内存模型1.程序计数器2.Java虚拟机栈3.本地方法栈4.Java堆5.方法区6.运行时常量池7.直接内存二、HotSpot虚拟机对象探秘1.对象的创建2.对象的内存布局3.对象的访问定位结尾 前言 对于从事C、 C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的“皇帝”,又是从事最基础工作的劳动人民——既拥有每一个对象的“所有权”,又担负着每一个对象生命从开始到终结的维护责任。 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作

少说,多做 1万+

深入理解java内存模型 一 基础 infoq,java内存模型系列教程

深入理解Java内存模型(一)——基础http://www.infoq.com/cn/articles/java-memory-model-1深入理解Java内存模型(二)——重排序http://www.infoq.com/cn/articles/java-memory-model-2/深入理解Java内存模型(三)——顺序一致性http://www.infoq.com/cn/articles/j...

weixin_28826961的博客 131

JMM Java 内存模型-参考《深入理解 Java 内存模型》一书

原文:http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/ 这是一篇讲 Java 内存模型比较系统和比较易懂的文章 基础 并发编程的模型分类 在并发编程需要处理的两个关键问题是:线程之间如何通信和线程之间如何同步。 通信 通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享...

football0的博客 221

Java面试之深入理解JVM一JVM内存模型

前言 JVM一直是java知识里面进阶阶段的重要部分,如果希望在java领域研究的更深入,则JVM则是如论如何也避开不了的话题,本系列试图通过简洁易读的方式,讲解JVM必要的知识点。 一、运行流程 我们都知道java一直宣传的口号是:一次编译,到处运行。那么它如何实现的呢?我们看下图: java程序经过一次编译之后,将java代码编译为字节码也就是class文件,然后在不同的操作系统上...

xinyuan2018的博客 1626

java内存模型 堆栈_深入理解Java内存模型(一)——基础

并发编程模型的分类在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的...

weixin_29806483的博客 404

深入理解Java内存模型《一》基础

并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之

我喂自己袋盐 1288
上一篇: 什么是Restful
下一篇: JavaBean中的绑定属性与限制属性
城
城
博客等级 码龄17年 4粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值