集体创作公约:自己申明创作的所有文章皆为本人原创,如果有参考任何文章的中央,会标注进去,如果有疏漏,欢送大家批评。如果大家发现网上有剽窃本文章的,欢送举报,并且踊跃向这个 github 仓库 提交 issue,谢谢反对~
本篇文章参考了大量文章,文档以及论文,然而这块货色真的很繁冗,我的程度无限,可能了解的也不到位,如有异议欢送留言提出。本系列会不断更新,联合大家的问题以及这里的谬误和疏漏,欢送大家留言
如果你喜爱单篇版,请拜访:全网最硬核 Java 新内存模型解析与试验单篇版(不断更新 QA 中)
如果你喜爱这个拆分的版本,这里是目录:
- 全网最硬核 Java 新内存模型解析与试验 – 1. 什么是 Java 内存模型
- 全网最硬核 Java 新内存模型解析与试验 – 2. 原子拜访与字决裂
- 全网最硬核 Java 新内存模型解析与试验 – 3. 硬核了解内存屏障(CPU+ 编译器)
- 全网最硬核 Java 新内存模型解析与试验 – 4. Java 新内存拜访形式与试验
- 全网最硬核 Java 新内存模型解析与试验 – 5. JVM 底层内存屏障源码剖析
JMM 相干文档:
- Java Language Specification Chapter 17
- The JSR-133 Cookbook for Compiler Writers – Doug Lea’s
- Using JDK 9 Memory Order Modes – Doug Lea’s
内存屏障,CPU 与内存模型相干:
- Weak vs. Strong Memory Models
- Memory Barriers: a Hardware View for Software Hackers
- A Detailed Analysis of Contemporary ARM and x86 Architectures
- Memory Model = Instruction Reordering + Store Atomicity
- Out-of-Order Execution
x86 CPU 相干材料:
- x86 wiki
- Intel® 64 and IA-32 Architectures Software Developer Manuals
- Formal Specification of the x86 Instruction Set Architecture
ARM CPU 相干材料:
- ARM wiki
- aarch64 Cortex-A710 Specification
各种一致性的了解:
- Coherence and Consistency
Aleskey 大神的 JMM 解说:
- Aleksey Shipilëv – 不要误会 Java 内存模型(上)
- Aleksey Shipilëv – 不要误会 Java 内存模型(下)
置信很多 Java 开发,都应用了 Java 的各种并发同步机制,例如 volatile,synchronized 以及 Lock 等等。也有很多人读过 JSR 第十七章 Threads and Locks(地址:https://docs.oracle.com/javase/specs/jls/se17/html/jls-17.html),其中包含同步、Wait/Notify、Sleep & Yield 以及内存模型等等做了很多标准解说。然而也置信大多数人和我一样,第一次读的时候,感觉就是在看热闹,看完了只是晓得他是这么规定的,然而为啥要这么规定,不这么规定会怎么样,并没有很清晰的意识。同时,联合 Hotspot 的实现,以及针对 Hotspot 的源码的解读,咱们甚至还会发现,因为 javac 的动态代码编译优化以及 C1、C2 的 JIT 编译优化,导致最初代码的体现与咱们的从标准上了解出代码可能的体现是不太统一的。并且,这种不统一,导致咱们在学习 Java 内存模型(JMM,Java Memory Model),了解 Java 内存模型设计的时候,如果想通过理论的代码去试,后果是与本人原本可能正确的了解被带偏了,导致误会。
我自己也是一直地尝试了解 Java 内存模型,重读 JLS 以及各路大神的剖析。这个系列,会梳理我集体在浏览这些标准以及剖析还有通过 jcstress 做的一些试验而得出的一些了解,心愿对于大家对 Java 9 之后的 Java 内存模型以及 API 形象的了解有所帮忙。然而,还是强调一点,内存模型的设计,出发点是让大家能够不必关怀底层而形象进去的一些设计,波及的货色很多,我的程度无限,可能了解的也不到位,我会尽量把每一个论点的论据以及参考都摆出来,请大家不要齐全置信这里的所有观点,如果有任何异议欢送带着具体的实例反驳并留言。
3. 原子性拜访
原子性拜访,对于一个字段的写入与读取,这个操作自身是原子的不可分割的。可能大家不常常关注的一点是依据 JLS 第 17 章中的阐明,上面这两个操作,并不是原子性拜访的:
因为大家以后的零碎通常都是 64 位的,得益于此,这两个操作大多是原子性的了。然而其实依据 Java 的标准,这两个并不是原子性的,在 32 位的零碎上就保障不了原子性。我这里间接援用 JLS 第 17 章的一段原话:
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
翻译过去,简略来说非 volatile 的 long 或者 double 可能会依照两次独自的 32 位写更新,所以是非原子性的。volatile 的 long 或者 double 读取和写入都是原子性的。
为了阐明咱们这里的原子性,我援用一个 jcstress 中的一个例子:
咱们应用 Java 8 32bit(Java 9 之后就不再反对 32 位的机器了)的 JVM 运行这里的代码,后果是:
能够看到,后果不止 -1 和 0 这种咱们代码中的指定的值,还有一些两头后果。
4. 字决裂(word tearing)
字决裂(word tearing)即你 更新一个字段,数组中的一个元素,会影响到另一个字段,数组中的另一个元素的值。例如处理器没有提供写单个 byte 的性能,假如最小维度是 int,在这样的处理器上更新 byte 数组,若只是简略地读取 byte 所在的整个 int,更新对应的 byte,而后将整个 int 再写回,这种做法是有问题的。Java 中没有字决裂景象,字段之间以及数组元素之间是独立的,更新一个字段或元素不能影响任何其它字段或元素的读取与更新。
为了阐明什么是字决裂,举一个不太失当的例子,即线程不平安的 BitSet。BitSet 的形象是比特位汇合(一个一个 0,1 这样,能够了解为一个 boolean 汇合),底层实现是一个 long 数组,一个 long 保留 64 个比特位,每次更新都是读取这个 long 而后通过位运算更新对应的比特位,再更新回去。接口层面是一位一位更新,然而底层却是依照 long 的维度更新的(因为是底层 long 数组),很显著,如果没有同步锁,并发拜访就会并发平安问题从而造成字决裂的问题:
后果是:
这里用了一个不太失当的例子来阐明什么是字决裂,Java 中是能够保障没有字决裂的,对应下面的 BitSet 的例子就是咱们尝试更新一个 boolean 数组,这样后果就只会是 true true:
这个后果只会是 true true
接下来,咱们将进入一个比拟苦楚的章节了,内存屏障,不过大家也不必太放心,从我集体的教训来看,内存屏障很难了解的起因是因为网上基本上不会从 Java 曾经为你屏蔽的底层细节去给你讲,间接了解会很难说服本人,于是就会猜测一些货色而后造成误会,所以本文不会上来丢给你 Doug Lea 形象的并始终沿用至今的 Java 四种内存屏障(就是 LoadLoad,StoreStore,LoadStore 和 StoreLoad 这四个,其实通过前面的剖析也能看进去,这四个内存屏障的设计对于当初的 CPU 来说曾经有些过期了,当初用的更多的是 acquire, release 以及 fence)心愿能通过笔者看的一些对于底层细节的文章论文中提取出便于大家了解的货色供大家参考,更好地更容易的了解内存屏障。
微信搜寻“我的编程喵”关注公众号,加作者微信,每日一刷,轻松晋升技术,斩获各种 offer:
我会常常发一些很好的各种框架的官网社区的新闻视频材料并加上集体翻译字幕到如下地址(也包含下面的公众号),欢送关注:
- 知乎:https://www.zhihu.com/people/…
- B 站:https://space.bilibili.com/31…