java内存屏障与JVM并发详解
JVM工作原理

JVM工作原理JVM(Java虚拟机)是Java程序的运行环境,它负责将Java源代码编译成可执行的字节码,并提供运行时环境来执行字节码。
JVM的工作原理涉及到类加载、内存管理、垃圾回收、即时编译等多个方面。
1. 类加载JVM通过类加载器(ClassLoader)来加载Java类。
类加载器根据类的全限定名(包括包名和类名)在类路径中查找对应的字节码文件,并将其加载到内存中。
类加载器采用双亲委派模型,即先由父类加载器尝试加载类,如果父类加载器无法加载,则由子类加载器尝试加载。
这种模型保证了类的唯一性和安全性。
2. 内存管理JVM将内存分为多个区域,包括方法区、堆、栈和程序计数器。
方法区存储类的元数据信息,如字段、方法、常量池等。
堆是存放对象实例的区域,通过垃圾回收机制来管理内存的分配和释放。
栈用于存储方法的局部变量和方法调用信息。
程序计数器用于指示当前线程执行的字节码指令。
3. 垃圾回收JVM通过垃圾回收机制自动回收不再使用的对象内存。
垃圾回收器会定期扫描堆内存,标记所有还在使用的对象,然后清理掉未被标记的对象。
常见的垃圾回收算法有标记-清除、复制、标记-整理等。
JVM还提供了不同的垃圾回收器,如Serial、Parallel、CMS、G1等,可以根据应用场景选择合适的垃圾回收器。
4. 即时编译JVM使用即时编译器(Just-In-Time Compiler)将热点代码(经常被执行的代码)编译成本地机器码,以提高执行效率。
JVM会监测程序的运行情况,根据热点代码的执行频率和调用关系进行优化编译。
即时编译器可以选择不同的编译策略,如解释执行、编译执行或混合执行。
5. 内存模型JVM定义了Java程序在多线程环境下的内存模型,保证多线程的内存可见性和有序性。
内存模型规定了线程之间如何进行通信和同步。
JVM使用主内存和工作内存的概念,线程之间的共享变量存储在主内存中,每个线程有自己的工作内存,线程对共享变量的操作先在工作内存中进行,然后通过主内存来同步和通信。
面试谈jvm原理

面试谈jvm原理Java虚拟机(JVM)是Java语言运行的基础。
JVM具有封装性、跨平台性、高度优化和可扩展性等特点,是Java应用程序的核心。
在Java的诞生初期,由于硬件环境和操作系统制约,JVM起到了垫底的作用。
而今天,JVM已经成为Java 运行效率和安全性的保障。
下面是一些我认为JVM原理面试时可能会涉及的重点:1. JVM的内存模型:JVM将内存分为堆内存和栈内存,堆内存用于存储对象实例和数组,而栈内存则用于存储方法的执行状态。
同时,JVM还有方法区和永久代的概念。
这些内存区域的大小和分配情况会影响JVM的性能和稳定性。
2. 垃圾回收机制:JVM的内存管理包括垃圾回收机制和内存分配机制。
垃圾回收机制是JVM实现自动内存管理的核心,JVM会周期性地扫描堆内存中没有被引用的对象,并自动回收它们所占用的内存。
垃圾回收机制常用的算法包括标记清除、复制和标记整理等。
3. 类加载机制:Java程序在运行时,需要将类文件中的二进制数据加载到JVM 中,才能执行相应的操作。
类加载机制将类文件加载到JVM中,并将它们解析为Java类。
类加载机制包括三个阶段:加载、链接和初始化。
4. JIT编译器:JIT(Just In Time)编译器是JVM在运行时动态优化的关键组件。
JIT编译器可以在程序运行时,根据代码的执行情况,生成本地机器代码,以提高程序的效率。
5. JVM调优:JVM的性能和稳定性很大程度上取决于JVM参数的设置和调整。
面试时,可能会涉及到如何根据系统的特点和需求,设置JVM参数以达到最佳性能和稳定性的问题。
总之,有关JVM原理的面试问题,往往涉及到JVM的内存模型、垃圾回收机制、类加载机制、JIT编译器和JVM调优等方面。
需要候选人对这些方面有比较深入的了解。
java处理并发的方法

java处理并发的方法
Java中处理并发的方法主要有以下几种:
1. 使用线程池:线程池是一种管理线程的方式,可以避免线程的创建和销毁的频繁操作,从而提高程序的并发性能。
Java中提供了System.out.println()方法的线程池实现,即System.out.println()方法可以被并发地调用,不会产生竞争条件。
2. 使用锁机制:锁机制可以保障多个线程对共享资源的互斥访问,避免竞争条件和数据不一致的问题。
Java中提供了原子变量和互斥量两种锁的实现方式。
原子变量是一个不可变的数据结构,可以保证多个线程同时访问它的值时不会出现竞争条件;互斥量可以确保多个线程同时访问共享资源时不会同时出现。
3. 使用并发编程模型:Java中的并发编程模型主要是
SMP(Single-Machine Precision)和MP(Multi-Machine Precision)模型,可以处理大规模数据和高并发访问。
SMP模型可以保证在同一台机器上多个线程同时访问相同的共享资源时不会出现竞争条件,而MP模型可以在不同机器上分配不同的计算资源来处理不同方向的计算任务。
4. 使用多路复用技术:多路复用技术可以让一个请求在多个计算任务之间多次转发,从而提高计算效率。
Java中提供了多路复用的实现方式,如Socket多路复用和URL多路复用。
以上是Java中处理并发的一些常见方法,具体应用需要根据具体场景进行选择。
java内存使用情况的命令

java内存使用情况的命令Java是一种面向对象的编程语言,它在开发应用程序时需要使用内存来存储数据和执行代码。
因此,了解Java的内存使用情况对于开发人员来说是非常重要的。
Java虚拟机(JVM)负责管理Java应用程序的内存,它使用垃圾回收机制来自动管理内存的分配和释放。
JVM的内存可以分为以下几个部分:1. 堆(Heap):堆是Java程序运行时动态分配的内存区域,用于存储对象实例。
堆的大小可以通过命令行参数-Xmx和-Xms来设置。
-Xms表示JVM启动时初始分配的堆内存大小,-Xmx表示堆能够达到的最大内存大小。
2. 方法区(Method Area):方法区用于存储已加载的类信息、常量、静态变量等数据。
方法区的大小可以通过命令行参数-XX:PermSize和-XX:MaxPermSize来设置。
-XX:PermSize表示JVM启动时初始分配的方法区大小,-XX:MaxPermSize表示方法区能够达到的最大大小。
3. 栈(Stack):栈用于存储Java方法中的局部变量以及方法调用时的状态信息。
每个Java线程都有一个独立的栈,栈的大小是固定的,并且在线程创建时被分配。
栈的大小可以通过命令行参数-Xss来设置。
除了上述部分,JVM还会使用一些额外的内存空间,如直接内存(DirectMemory)和本地方法栈(Native Method Stack),用于存储一些特殊的数据和执行本地方法。
了解Java的内存使用情况对于定位内存泄漏和优化程序性能非常有帮助。
下面是几个常用的命令,可以用于监控和调整Java程序的内存使用情况:1. jps:该命令用于列出当前运行的Java进程,以及对应的进程ID。
2. jstat:该命令用于监控Java虚拟机的各种运行状态,包括堆的使用情况、类加载数量、垃圾回收情况等。
常用的参数包括-jstat -gcutil <pid>和-jstat-gccapacity <pid>。
Java应用中的并发控制

Java应用中的并发控制在Java应用程序的开发中,处理并发控制是一项至关重要的任务。
并发控制是指在多个线程同时访问共享资源时,对资源的正确使用和保护。
如果不对并发进行有效的控制,就可能导致数据不一致、竞态条件等问题。
1. 并发控制的概念并发控制是指在多线程环境下,为了保证数据的一致性和正确性,对共享资源的访问进行合理的调度和管理。
在Java应用程序中,共享资源可以是变量、对象、数据结构等,通过合理的并发控制手段,可以保证多线程间的正确协作,提高程序的性能和可靠性。
2. Java中的并发控制手段Java提供了多种并发控制手段和工具,以下是其中几种常用的手段:- synchronized关键字:通过在方法或代码块中使用synchronized关键字,可以实现对共享资源的互斥访问。
synchronized关键字可以保证在同一时间只有一个线程可以访问带有synchronized关键字的代码块,其他线程需要等待该线程执行完毕才能访问。
- Lock接口:Lock接口提供了更加灵活和细粒度的锁定机制。
通过使用Lock接口的实现类,如ReentrantLock,可以实现更高级的并发控制策略,比如可重入锁、公平锁等。
Lock接口还提供了更多的方法,如tryLock(),可以尝试获取锁而不阻塞线程。
- Atomic包:Atomic包下提供了一系列的原子操作类,如AtomicInteger、AtomicLong等。
这些类提供了具备原子性的操作方法,保证了对共享资源的读取、修改等操作的线程安全。
- 并发集合类:Java提供了诸如ConcurrentHashMap、ConcurrentLinkedQueue等并发集合类,用于在多线程环境下对集合进行并发操作。
这些类通过内部实现机制,可以保证多线程对集合的安全访问。
3. 并发控制的最佳实践在Java应用程序中,处理并发控制需要遵循一些最佳实践,以确保程序的正确性和性能。
- 选择合适的并发控制手段:根据具体的场景和需求,选择适合的并发控制手段。
jvm的工作流程

jvm的工作流程JVM的工作流程一、概述JVM(Java Virtual Machine)是Java虚拟机的缩写,它是Java 语言的核心和基础。
JVM的工作流程是指在程序运行过程中,JVM 如何将Java源代码编译成可执行的机器码并运行的整个过程。
本文将详细介绍JVM的工作流程。
二、Java源代码编译在JVM的工作流程中,首先需要将Java源代码编译成字节码文件(.class文件)。
Java源代码由Java编译器(javac)编译成字节码,字节码是一种中间编译语言,具有与特定平台无关的特性。
三、类加载在JVM的工作流程中,类加载是指将字节码文件加载到JVM中并转换成Java能够识别的数据结构。
JVM通过类加载器(ClassLoader)来完成类的加载工作。
类加载器将字节码文件加载到JVM的方法区中,并在堆区中创建一个Class对象来表示该类。
四、字节码解释与执行在JVM的工作流程中,字节码解释与执行是JVM的核心功能。
JVM通过解释器将字节码指令逐条解释成对应的机器码并执行。
解释器负责解释字节码并执行相应的操作,将结果保存在操作数栈和局部变量表中。
五、即时编译在JVM的工作流程中,即时编译是JVM的优化手段之一。
即时编译器(Just-In-Time Compiler,JIT)将热点代码(频繁执行的代码)编译成本地机器码,以提高程序的执行效率。
即时编译器可以根据代码的执行情况来进行优化,将频繁执行的代码编译成机器码后再执行,从而提高程序的性能。
六、垃圾回收在JVM的工作流程中,垃圾回收是JVM的另一个重要功能。
垃圾回收器(Garbage Collector,GC)负责回收不再使用的内存空间,释放给其他程序使用。
垃圾回收器通过标记-清除、复制、标记-整理等算法来回收内存。
垃圾回收器可以自动管理内存,减少程序员的内存管理工作。
七、内存模型在JVM的工作流程中,内存模型是JVM的基础。
JVM将内存划分为不同的区域,包括方法区、堆区、栈区和本地方法栈等。
java jvm 空间担保机制

java jvm 空间担保机制摘要:1.引言2.JVM 空间担保机制的概念3.JVM 空间担保机制的工作原理4.JVM 空间担保机制的优势5.总结正文:Java 虚拟机(JVM)是Java 语言的核心组件,负责充当Java 字节码和底层操作系统之间的中间层。
在JVM 中,空间担保机制是一个重要的特性,它有助于确保Java 程序在运行过程中能够获得足够的内存资源。
接下来,我们将详细介绍JVM 空间担保机制的概念、工作原理、优势等方面的内容。
1.引言在Java 程序运行过程中,JVM 需要为每个线程分配一定的内存空间,以存储程序的运行数据。
然而,由于程序运行的复杂性和不确定性,JVM 在为线程分配内存时可能会遇到资源不足的问题。
为了解决这个问题,JVM 引入了空间担保机制,以提供一种可靠的方式来管理内存资源。
2.JVM 空间担保机制的概念JVM 空间担保机制是一种在运行时确保Java 程序能够获得足够内存资源的方法。
它通过为每个线程分配一个虚拟的内存空间(也称为内存堆)来实现这一目标。
在这个内存堆中,线程可以存储和管理其运行时需要的数据。
JVM空间担保机制的主要目标是确保线程始终有足够的内存资源来执行任务,同时避免内存资源的浪费。
3.JVM 空间担保机制的工作原理JVM 空间担保机制的工作原理可以分为以下几个步骤:- 初始化:在Java 程序启动时,JVM 会为每个线程创建一个内存堆。
这个内存堆的初始大小由JVM 参数(如-Xms)指定。
- 分配:当线程需要内存资源时,JVM 会根据当前堆的大小和线程所需的资源量进行内存分配。
如果堆空间充足,JVM 会直接为线程分配内存;否则,JVM 会触发内存回收操作,释放不再使用的内存空间,然后再次尝试为线程分配内存。
- 回收:当线程不再需要某些内存资源时,JVM 会通过垃圾回收(GC)算法来回收这些资源。
垃圾回收算法的目标是通过释放不再使用的内存空间来增加堆的大小,以便为其他线程分配更多的内存资源。
JVM运行机制及其原理

JVM运行机制及其原理JVM(Java Virtual Machine)是Java虚拟机的缩写,是运行Java 字节码的虚拟计算机。
它是Java平台的核心组件,负责在不同的操作系统上执行Java程序。
JVM运行机制主要包括类加载、字节码解释、即时编译、垃圾收集等过程,下面将详细介绍JVM运行机制及其原理。
1.类加载当一个Java程序被运行时,JVM会首先加载程序的主类,然后根据程序的依赖关系逐步加载相关的类。
类加载过程主要分为加载、验证、准备、解析和初始化几个阶段:-加载:通过类加载器将类文件加载到内存中。
- 验证:确保加载的类符合Java语言规范和JVM规范。
-准备:为类的静态变量分配内存并初始化为默认值。
-解析:将符号引用转换为直接引用。
-初始化:执行类的初始化方法。
2.字节码解释加载完类文件后,JVM会通过解释器将字节码文件逐条解释执行,将每条字节码翻译成对应的机器代码并执行。
这种方式简单直接,但效率较低,适用于少量代码和频繁切换的情况。
3.即时编译4.垃圾收集JVM还负责管理程序的内存,包括分配内存、回收无用内存等。
在Java中,内存是通过堆和栈来管理的,堆用于存放对象实例,栈用于存放基本数据类型和方法调用。
JVM通过垃圾收集器来管理堆内存,自动回收不再使用的对象,并将内存释放出来供其他对象使用。
5.类加载器类加载器是JVM的重要组成部分,负责加载class文件,并将其转换成JVM可以识别的数据结构。
JVM中存在多个类加载器,分为三个级别:启动类加载器、扩展类加载器和应用程序类加载器。
类加载器采用双亲委派模型,当需要加载一个类时,先委托给父类加载器加载,只有当父类加载器无法找到类时,才由自己加载。
6.内存模型JVM中的内存分为程序计数器、虚拟机栈、本地方法栈、堆、方法区等几部分。
程序计数器记录当前指令执行的位置;虚拟机栈用于存放局部变量表和操作数栈;本地方法栈用于支持本地方法调用;堆用于存放对象实例;方法区用于存放类信息、静态变量等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
深入Java底层:内存屏障与JVM并发详解(1)本文介绍了内存屏障对多线程程序的影响,同时将研究内存屏障与JVM并发机制的关系,如易变量(volatile)、同步(synchronized)和原子条件式(atomic conditional)。
AD:内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。
本文假定读者已经充分掌握了相关概念和Java内存模型,不讨论并发互斥、并行机制和原子性。
内存屏障用来实现并发编程中称为可见性(visibility)的同样重要的作用。
关于JVM更多内容,请参阅:JVM详解 Java虚拟机原理与优化内存屏障为何重要?对主存的一次访问一般花费硬件的数百次时钟周期。
处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序。
也就是说,程序的读写操作不一定会按照它要求处理器的顺序执行。
当数据是不可变的,同时/或者数据限制在线程范围内,这些优化是无害的。
如果把这些优化与对称多处理(symmetric multi-processing)和共享可变状态(shared mutable state)结合,那么就是一场噩梦。
当基于共享可变状态的内存操作被重新排序时,程序可能行为不定。
一个线程写入的数据可能被其他线程可见,原因是数据写入的顺序不一致。
适当的放置内存屏障通过强制处理器顺序执行待定的内存操作来避免这个问题。
内存屏障的协调作用内存屏障不直接由JVM暴露,相反它们被JVM插入到指令序列中以维持语言层并发原语的语义。
我们研究几个简单Java程序的源代码和汇编指令。
首先快速看一下Dekker算法中的内存屏障。
该算法利用volatile变量协调两个线程之间的共享资源访问。
请不要关注该算法的出色细节。
哪些部分是相关的?每个线程通过发信号试图进入代码第一行的关键区域。
如果线程在第三行意识到冲突(两个线程都要访问),通过turn变量的操作来解决。
在任何时刻只有一个线程可以访问关键区域。
1. // code run by first thread // code run by second thread2.3. 1 intentFirst = true; intentSecond = true;4. 25. 3 while (intentSecond) while (intentFirst) // volatile read6. 4 if (turn != 0) { if (turn != 1) { // volatile read7. 5 intentFirst = false; intentSecond = false;8. 6 while (turn != 0) {} while (turn != 1) {}9. 7 intentFirst = true; intentSecond = true;10. 8 } }11. 912.10 criticalSection(); criticalSection();13.1114.12 turn = 1; turn = 0; // volatile write15.13 intentFirst = false; intentSecond = false; // volatile write硬件优化可以在没有内存屏障的情况下打乱这段代码,即使编译器按照程序员的想法顺序列出所有的内存操作。
考虑第三、四行的两次顺序volatile读操作。
每一个线程检查其他线程是否发信号想进入关键区域,然后检查轮到谁操作了。
考虑第12、13行的两次顺序写操作。
每一个线程把访问权释放给其他线程,然后撤销自己访问关键区域的意图。
读线程应该从不期望在其他线程撤销访问意愿后观察到其他线程对turn变量的写操作。
这是个灾难。
但是如果这些变量没有 volatile修饰符,这的确会发生!例如,没有volatile修饰符,第二个线程在第一个线程对turn执行写操作(倒数第二行)之前可能会观察到第一个线程对intentFirst(倒数第一行)的写操作。
关键词volatile避免了这种情况,因为它在对turn变量的写操作和对 intentFirst变量的写操作之间创建了一个先后关系。
编译器无法重新排序这些写操作,如果必要,它会利用一个内存屏障禁止处理器重排序。
让我们来看看一些实现细节。
PrintAssembly HotSpot选项是JVM的一个诊断标志,允许我们获取JIT编译器生成的汇编指令。
这需要最新的OpenJDK版本或者新HotSpot update14或者更高版本。
通过需要一个反编译插件。
Kenai项目提供了用于Solaris、Linux和BSD的插件二进制文件。
hsdis 是另一款可以在Windows通过源码构建的插件。
两次顺序读操作的第一次(第三行)的汇编指令如下。
指令流基于Itanium 2多处理硬件、JDK 1.6 update 17。
本文的所有指令流都在左手边以行号标记。
相关的读操作、写操作和内存屏障指令都以粗体标记。
建议读者不要沉迷于每一行指令。
1. 1 0x2000000001de819c: adds r37=597,r36;; ; (84112554)2. 2 0x2000000001de81a0: ld1.acq r38=[r37];; ;...0b30014a a0103. 3 0x2000000001de81a6: nop.m 0x0 ;...00000002 00c04. 4 0x2000000001de81ac: sxt1 r38r38=r38;; ; (00513004)5. 5 0x2000000001de81b0: cmp4.eq p0,p6=0,r38 ;...1100004c 86396. 6 0x2000000001de81b6: nop.i 0x0 ;...00000002 00037.7 0x2000000001de81bc: br.cond.dpnt.many 0x2000000001de8220;简短的指令流其实内容丰富。
第一次volatile位于第二行。
Java内存模型确保了JVM 会在第二次读操作之前将第一次读操作交给处理器,也就是按照“程序的顺序”——但是这单单一行指令是不够的,因为处理器仍然可以自由乱序执行这些操作。
为了支持Java内存模型的一致性,JVM在第一次读操作上添加了注解ld.acq,也就是“载入获取”(load acquire)。
通过使用ld.acq,编译器确保第二行的读操作在接下来的读操作之前完成,问题就解决了。
请注意这影响了读操作,而不是写。
内存屏障强制读或写操作顺序限制不是单向的。
强制读和写操作顺序限制的内存屏障是双向的,类似于双向开的栅栏。
使用ld.acq就是单向内存屏障的例子。
一致性具有两面性。
如果一个读线程在两次读操作之间插入了内存屏障而另外一个线程没有在两次写操作之间添加内存屏障又有什么用呢?线程为了协调,必须同时遵守这个协议,就像网络中的节点或者团队中的成员。
如果某个线程破坏了这个约定,那么其他所有线程的努力都白费。
Dekker算法的最后两行代码的汇编指令应该插入一个内存屏障,两次volatile写之间。
1.$ java -XX:+UnlockDiagnosticVMOptions -XX:PrintAssemblyOptions=hsdis-print-bytes2.-XX:CompileCommand=print,WriterReader.write WriterReader3. 1 0x2000000001de81c0: adds r37=592,r36;; ;...0b284149 04214. 2 0x2000000001de81c6: st4.rel [r37]=r39 ;...00389560 23805. 3 0x2000000001de81cc: adds r36=596,r36;; ; (84112544)6. 4 0x2000000001de81d0: st1.rel [r36]=r0 ;...09000048 a0117. 5 0x2000000001de81d6: mf ;...00000044 00008. 6 0x2000000001de81dc: nop.i 0x0;; ; (00040000)9. 7 0x2000000001de81e0: mov r12=r33 ;...00600042 002110. 8 0x2000000001de81e6: mov.ret b0=r35,0x2000000001de81e011. 9 0x2000000001de81ec: mov.i ar.pfs=r34 ;...00aa022012.10 0x2000000001de81f0: mov r6=r32 ;...09300040 0021这里我们可以看到在第四行第二次写操作被注解了一个显式内存屏障。
通过使用st.rel,即“存储释放”(store release),编译器确保第一次写操作在第二次写操作之前完成。
这就完成了两边的约定,因为第一次写操作在第二次写操作之前发生。
st.rel屏障是单向的——就像ld.acq一样。
但是在第五行编译器设置了一个双向内存屏障。
mf指令,或者称为“内存栅栏”,是Itanium 2指令集中的完整栅栏。
笔者认为是多余的。
深入Java底层:内存屏障与JVM并发详解(2)本文介绍了内存屏障对多线程程序的影响,同时将研究内存屏障与JVM并发机制的关系,如易变量(volatile)、同步(synchronized)和原子条件式(atomic conditional)。
AD:内存屏障是特定于硬件的本文不想针对所有内存屏障做一综述。
这将是一件不朽的功绩。
但是,重要的是认识到这些指令在不同的硬件体系中迥异。
下面的指令是连续写操作在多处理 Intel Xeon硬件上编译的结果。
本文后面的所有汇编指令除非特殊声明否则都出自于Intel Xeon。
1. 1 0x03f8340c: push %ebp ; (55)2. 2 0x03f8340d: sub $0x8,%esp ;...81ec0800 00003. 3 0x03f83413: mov $0x14c,%edi ;...bf4c0100 004. 4 0x03f83418: movb $0x1,-0x505a72f0(%edi) ;...c687108d a5af015. 5 0x03f8341f: mfence ;...0faef06. 6 0x03f83422: mov $0x148,%ebp ;...bd480100 007. 7 0x03f83427: mov $0x14d,%edx ;...ba4d0100 008. 8 0x03f8342c: movsbl -0x505a72f0(%edx),%ebx ;...0fbe9a10 8da5af9. 9 0x03f83433: test %ebx,%ebx ;...85db10.10 0x03f83435: jne 0x03f83460 ; (7529)11.11 0x03f83437: movl $0x1,-0x505a72f0(%ebp) ;...c785108d a5af0112.12 0x03f83441: movb $0x0,-0x505a72f0(%edi) ;...c687108d a5af0013.13 0x03f83448: mfence ;...0faef014.14 0x03f8344b: add $0x8,%esp ;...83c40815.15 0x03f8344e: pop %ebp ;...5d我们可以看到x86 Xeon在第11、12行执行两次volatile写操作。