设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 我们致力于打造专业的站长资讯、交流、合作平台!
热搜: 手机 曝光 苹果
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

发布时间:2019-11-08 17:32 所属栏目:[优化] 来源:平头哥
导读:Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你碰到了半吊子面试官,那就要恭喜你了。Java 内存模

Java 内存模型跟上一篇 JVM 内存结构很像,我经常会把他们搞混,但其实它们不是一回事,而且相差还很大的,希望你没它们搞混,特别是在面试的时候,搞混了的话就会答非所问,影响你的面试成绩,当然也许你碰到了半吊子面试官,那就要恭喜你了。Java 内存模型比 JVM 内存结构复杂很多,Java 内存模型有一个规范叫:《JSR 133 :Java 内存模型与线程规范》,里面的内容很丰富,如果你没看过的话,我建议你看一下。今天我们就简单的来聊一聊 Java 内存模型,关于 Java 内存模型,我们还是先从硬件内存模型入手。

硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

硬件内存模型

先来看看硬件内存简单架构,如下图所示:

硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

硬件内存结构

这是一幅简单的硬件内存结构图,真实的结构图要比这复杂很多,特别是在缓存层,现在的计算机中 CPU 缓存一般有三层,你也可以打开你的电脑看看,打开 任务资源管理器 ---> 性能 ---> cpu ,如下图所示:

硬件内存模型到 Java 内存模型,这些硬核知识你知多少? 

CPU 缓存

从图中可以看出我这台机器的 CPU 有三级缓存,一级缓存 (L1) 、二级缓存(L2)、三级缓存(L3),一级缓存是最接近 CPU 的,三级缓存是最接近内存的,每一级缓存的数据都是下一级缓存的一部分。三级缓存架构如下图所示:

硬件内存模型到 Java 内存模型,这些硬核知识你知多少? 

图片来源网络

现在我们对硬件内存架构有了一定的了解,我们来弄明白一个问题,为什么需要在 CPU 和内存之间添加缓存?

关于这个问题我们就简单点说,我们知道 CPU 是高速的,而内存相对来说是低速的,这就会造成一个问题,不能充分的利用 CPU 高速的特点,因为 CPU 每次从内存里获取数据的话都需要等待,这样就浪费了 CPU 高速的性能,缓存的出现就是用来消除 CPU 与内存之间差距的。缓存的速度要大于内存小于 CPU ,加入缓存之后,CPU 直接从缓存中读取数据,因为缓存还是比较快的,所以这样就充分利用了 CPU 高速的特性。但也不是每次都能从缓存中读取到数据,这个跟我们项目中使用的 redis 等缓存工具一样,也存在一个缓存命中率,在 CPU 中,先查找 L1 Cache,如果 L1 Cache 没有命中,就往 L2 Cache 里继续找,依此类推,最后没找到的话直接从内存中取,然后添加到缓存中。当然当 CPU 需要写数据到主存时,同样会先刷新寄存器中的数据到 CPU 缓存,然后再把数据刷新到主内存中。

也许你已经看出了这个框架的弊端,在单核时代只有一个处理器核心,读/写操作完全都是由单核完成,没什么问题;但是多核架构,一个核修改主存后,其他核心并不知道数据已经失效,继续傻傻的使用主存或者自己缓存层的数据,那么就会导致数据不一致的情况。关于这个问题 CPU 硬件厂商也提供了解决办法,叫做缓存一致性协议(MESI 协议),缓存一致性协议这东西我也不了解,我也说不清,所以就不在这里 BB 了,有兴趣的可以自行研究。

聊完了硬件内存架构,我们将焦点回到我们的主题 Java 内存模型上,下面就一起来聊一聊 Java 内存模型。

Java 内存模型

Java 内存模型是什么?Java 内存模型可以理解为遵照多核硬件架构的设计,用 Java 实现了一套 JVM 层面的“缓存一致性”,这样就可以规避 CPU 硬件厂商的标准不一样带来的风险。好了,正式介绍一下 Java 内存模型:Java 内存模型 ( Java Memory Model,简称 JMM ),本身是种抽象的概念,并不是像硬件架构一样真实存在的,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量 (包括实例字段、静态字段和构成数组对象的元素) 的访问方式,更多关于 Java 内存模型知识可以阅读 JSR 133 :Java 内存模型与线程规范。

我们知道 JVM 运行程序的实体是线程,在上一篇 JVM 内存结构中我们得知每个线程创建时,JVM 都会为其创建一个工作内存 ( Java 栈 ),用于存储线程私有数据,而 Java 内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作 ( 读取赋值等 ) 必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完后再将变量写回主内存,不能直接操作主内存中的变量。

我们知道 Java 栈是每个线程私有的数据区域,别的线程无法访问到不同线程的私有数据,所以线程需要通信的话,就必须通过主内存来完成,Java 内存模型就是夹在这两者之间的一组规范,我们先来看看这个抽象架构图:

硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

图片来源网络

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

  1. 首先,线程 A 把本地内存 A 中的共享变量副本中的值刷新到主内存中去。
  2. 然后,线程 B 到主内存中去读取线程 A 更新之后的值,这样线程 A 中的变量值就到了线程 B 中。

我们来看一个具体的例子来加深一下理解,看下面这张图:

硬件内存模型到 Java 内存模型,这些硬核知识你知多少?

图片来源网络

现在线程 A 需要和线程 B 通信,我们已经知道线程之间通信的两部曲了,假设初始时,这三个内存中的 x 值都为 0。线程 A 在执行时,把更新后的 x 值(假设值为 1)临时存放在自己的本地内存 A 中。当线程 A 和线程 B 需要通信时,线程 A 首先会把自己本地内存中修改后的 x 值刷新到主内存中,此时主内存中的 x 值变为了 1。随后,线程 B 到主内存中去读取线程 A 更新后的 x 值,此时线程 B 的本地内存的 x 值也变为了 1,这样就完成了一次通信。

(编辑:ASP站长网)

网友评论
推荐文章