乐趣区

关于java:一个试图了解JVM内存模型的两年经验的初级程序员透彻

所有的编程语言中都有内存模型这个概念,区别于微架构的内存模型,高级语言的内存模型包含了编译器和微架构两局部。我试图理解了 Java、C# 和 Go 语言的内存模型,发现内容根本大同小异,只是这些语言在具体实现的时候略有不同。

咱们来看看 Java 内存模型吧,提到 Java 内存模型大家对这个图肯定十分相熟:

这张图通知咱们在线程运行的时候有一个内存专用的一小块内存,当 Java 程序会将变量同步到线程所在的内存,这时候会操作工作内存中的变量,而线程中变量的值何时同步回主内存是不可预期的。但同时 Java 内存模型又通知咱们通过应用关键词“synchronized”或“volatile”能够让 Java 保障某些束缚:

“volatile”— 保障读写的都是主内存的变量
“synchronized”—保障在块开始时都同步主内存的值到工作内存,而块完结时将变量同步回主内存

通过以上形容咱们就能够写出线程平安的 Java 程序,JDK 也同时帮咱们屏蔽了很多底层的货色。

但当你深刻理解 JVM 的时候你会发现基本就没有工作内存这个货色,即内存中基本不会调配这么一块空间来运行你的 Java 程序,那么工作内存到底是什么货色呢?

这个问题也已经困扰了我很长时间,因为我素来没有从 JVM 的实现中找到过和主内存同步的代码,因为当应用“volatile”时我仅仅能从源代码中调用了这行语句:

__asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");

而这个指令在局部微架构上的次要性能就是避免指令重排,即这条指令前后的其它指令不会越过这个界线执行 [注 1]。

在当初的 x86/x64 微架构中读写内存的一致性都是通过 MESI(Intel 应用 MESI-F,AMD 应用 MOESI)协定保障 [注 2],MESI 的状态转换图如下:

那 Java 内存模型中所说的工作内存是什么呢?
我的了解是,首先“工作内存”是一个虚构的概念,而承载这个概念次要是两局部:

1. 编译器
2. 微架构 

作为编译器必定是执行速度越快越好,所以作为编译器该当尽量减少从内存读数据,如果一个数据在寄存器中,那么间接应用寄存器中的值无疑性能是最高的,但同时这也会导致可能读不到最新的值,这里咱们通过在 Java 语言中为变量加上“volatile”强制通知编译器这个变量肯定要从内存取得,这时编译器即不会做此类优化【案例见参考资料 5(是一个.Net 的例子)】。

对于微架构来说,在 x86/x64 下,CPU 会在执行指令时做指令重排,即编译器生成的指令程序和真正在 CPU 执行的程序可能是不统一的。当咱们用一个变量做信号的时候这种指令重排会带来喜剧,即如果有如下代码:

x = 0;
y = 0;
i = 0;
j = 0;
// thread A
y = 1;
x = 1;
// thread B
i = x;
j = y;

下面的代码 i 和 j 的值会是多少呢?答案是:“00,01,10,11”都是有可能的。
对于这种状况,如果咱们想得到确定的后果则须要通过“synchronized”(或者 j.c.u.locks)来做线程间同步。

所以,我集体对 Java 内存模型的了解是:在编译器各种优化及多种类型的微架构平台上,Java 语言标准制定者试图创立一个虚构的概念并传递到 Java 程序员,让他们可能在这个虚构的概念上写出线程平安的程序来,而编译器实现者会依据 Java 语言标准中的各种束缚在不同的平台上达到 Java 程序员所须要的线程平安这个目标。

最初

私信回复 材料 支付一线大厂 Java 面试题总结 + 阿里巴巴泰山手册 + 各知识点学习思维导 + 一份 300 页 pdf 文档的 Java 外围知识点总结!

这些材料的内容都是面试时面试官必问的知识点,篇章包含了很多知识点,其中包含了有基础知识、Java 汇合、JVM、多线程并发、spring 原理、微服务、Netty 与 RPC、Kafka、日记、设计模式、Java 算法、数据库、Zookeeper、分布式缓存、数据结构等等。

退出移动版