计算机内存架构 & JMM

总结下计算机内存架构的基本原理以及JMM的不同之处。

内存架构

内存架构

现代内存架构如上图所示。

高速缓存存在的意义?

计算机的存储设备处理器的运算速度之间有着几个数量级的差距。

由于处理器和内存之间的性能差距,当代所有计算机系统都加入了一层高速缓存来间接缓冲数据(可以理解为业务中使用的本地缓存、分布式缓存等)。

访问速度比较寄存器 > 高速缓存 > 主内存,有些计算机架构中存在多级缓存(例如,Linux多级缓存)。

多级缓存

计算机提供了多级缓存来提高系统的读写效率。

多级缓存


Core 内部:

CPU内部意味着L0、L1、L2都属于CPU的一部分,因此每一个CPU都拥有独占的LO、L1、L2。

  • L0:寄存器
  • L1:L1高速缓存(SRAM),划分为一级数据缓存(L1 D-Cache)和一级指令缓存(L1 I-Cache)
  • L2:L2高速缓存(SRAM)

Core 共享:

  • L3:L3高速缓存(SRAM),会造成伪共享(False Sharing)
  • L4:主内存(DRAM)
  • L5:本地二级存储(本地磁盘)
  • L6:远程二级存储(分布式文件系统)

多级缓存

从图中可以看到,每个处理器都有自己的高速缓存,而它们又共享同一主内存。

当多个处理器的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。

缓存一致性

高速缓存类似一种代理模式,在CPU和主内存质之间构建一条高速公路,但也会造成缓存一致性问题。

当多个处理器同时共享同一块主内存时,将可能导致高速缓存的一致性问题

为了解决这个问题,提出了缓存一致性协议,常见的协议包括MSI、MESI等。

缓存一致性的大体思想:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

伪共享

CPU缓存系统是按缓存行(Cache Line)为单位存储的。

缓存行(Cache Line):CPU缓存的最小单位,大小一般为64bytes。

CPU加载主内存到缓存是按照缓存行来加载的,也就是说,一个缓存行可以存储多个变量。

在多线程情况下,如果需要修改共享在同一个缓存行的变量,也会造成共享缓存行中其他变量的缓存失效,从而影响彼此的性能,这就是伪共享(False Sharing)

解决方法:利用单个数据填充一个缓存行,空间换时间的方式,避免多个变量共享同一个缓存行。

Java8中新增了一个注解`@sun.misc.Contended`用于解决伪共享的问题。

`@sun.misc.Contended`:通过在对象或字段前后追加padding,使数据长度达到128bytes(2被缓存行的大小),从而避免相邻扇区预取导致的伪共享冲突。

具体参考:Java8使用@sun.misc.Contended避免伪共享

指令重排

即使存在高速缓存也不能把CPU的性能充分利用,因此提出了指令重排

指令重排:处理器会对输入的代码进行乱序优化,在保证结果不变的情况下,优化指令排序,从而实现CPU的充分利用。

JMM

JMM的全程是Java内存模型(Java Memory Model)

映射关系

JMM与硬件内存架构是不同的,区别在于:硬件内存架构未区分线程栈和堆

JMM工作内存(Working Memory)是一个抽象概念,涵盖了CPU寄存器、高速缓存等。

Java内存模型

可见性

可见性:基于工作内存,在多线程的情况下,任意线程修改了共享变量的值,其他线程能够同步感知到变量的变化。

Java提供了volatile来解决内存可见性问题。

当操作发生在volatile变量时,有如下规则:

  • JMM会把该线程对应的工作内存中的共享变量值刷新到主内存;
  • JMM会把该线程对应的工作内存置为无效并从主内存中读取共享变量。

读写volatile变量会导致变量从主内存读写,这远比从CPU缓存读写更加耗时。

内存屏障

内存屏障:不仅可以保证屏障前后的指令顺序,也可以保证内存数据的可见性。

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

volatile也提供了禁止指令重排序的功能。

参考

https://zhuanlan.zhihu.com/p/29881777
https://blog.csdn.net/muxiqingyang/article/details/6615199
https://www.yuque.com/crow/xriqpk/hm5s0s?language=en-us