JAVA程序员需要知道的计算机底层基础02-CPU组成原理

CPU的基本组成

Program Counter 程序计数器 (记录当前指令地址)

可以理解为我们的指令在内存中相当于一个字节数组,程序计数器记录的就是当前指令的内存地址,便于每次根据当前操作指令的占用大小进行向后移动。

Registers 寄存器

cpu中有许多不同的寄存器。因为数据每次都从内存去取太慢了,所以把从内存取来的数据暂时存储在寄存器,便于CPU计算快速拿到需要用到的数据。现在说的64位计算机,它的每一个寄存器就是可以存储64位的数据。
JVM中的栈帧中的本地局部变量表其实就相当于“寄存器”的存在,但它是通过内存实现的,而这个寄存器是硬件级别的,效率要高的多。

ALU ( Arithmetic & Logic Unit) 逻辑运算单元

就是用来进行运算的地方,举个例子,如果要计算2+3:

1、把2和3分别从内存取出来,把2 move到AX寄存器,把3 move到在BX寄存器,
2、通过程序计数器读取当前的指令,是add。
3、ALU发现是add指令,固定的从AX寄存器读取一个数2,从BX寄存器读取一个数3,然后进行一系列的电路运算,得出运算结果5,将5输出给另外一个寄存器DX
4、寄存器DX通过输出指令将5输出到内存的某个位置,完成本次运算。

CU ( Control Unit) 控制单元

就是对一些比如中断信号的控制。

MMU (Memory Management Unit) 内存管理单元

最早的内存管理单元都是由操作系统实现的,现在都是硬件+操作系统实现的。

Cache 缓存

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

内存空间大,但是太远所以太慢(CPU速度和内存速度100:1),寄存器快是快,空间又太小,折合之下,就多了cache的概念,cache根据级别的不同,离cpu的距离也不同,越近的存储空间越小,性能越高,价格越贵。
JAVA程序员需要知道的计算机底层基础02-CPU组成原理

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

局部性原理,就是说程序读取了一块地方的数据,那它相邻的后续地址的数据也很大概率会被读取。

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

为什么只有三级缓存? 工业测试结果这是最佳的方案。

这里因此是成块读取,CPU的缓存可能会出现伪共享问题。
因此出现了缓存行对齐:对于有些特别敏感的数字,会存在线程高竞争的访问,为了保证不发生伪共享,可以使用缓存航对齐的编程方式:
JDK7中,很多采用long padding提高效率
JDK8,加入了@Contended注解,注意JVM参数需要加上:JVM -XX:-RestrictContended

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

除了MESI,其他CPU还有其他的缓存一致性协议。

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

cpu缓存行的大小也是工业测试决定的,intel的最终测试决定是64个字节,但其他cpu可能未必。

超线程

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

有时候听到说有的cpu支持运行多个线程,这样的cpu叫做超线程,那什么叫超线程?

首先要先了解下普通的cpu的执行过程。

线程切换

正常来说,一个cpu中只会有一组寄存器和一个程序计数器。 假如多线程同时运行的情况下,线程1先由cpu执行着,寄存器和程序计数器都是放的是关于线程1的内容。此时线程2时间片到了,进来了,它会先把CPU中的寄存器和程序计数器的关于线程1的内容放入缓存中,然后再才能开始跑线程2的东西,这个过程就叫线程切换,可想而知,这个切换的过程肯定是消耗cpu的性能的。

超线程的不同

因此很简单,超线程就是让一个cpu中对应多组寄存器和多个程序计数器,这样多个线程进来的时候,不用再进行切换,而是各自占上空余的寄存器组合程序计数器槽位就行了。

乱序执行

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

https://preshing.com/20120515/memory-reordering-caught-in-the-act/

java中的指令重排序

JAVA程序员需要知道的计算机底层基础02-CPU组成原理
JAVA程序员需要知道的计算机底层基础02-CPU组成原理
JAVA程序员需要知道的计算机底层基础02-CPU组成原理
JAVA程序员需要知道的计算机底层基础02-CPU组成原理

如何禁止重排序?

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

volatile底层实现是 lock addl 0x0 (esp),在esp寄存器加个0,加0其实没有任何意义,实际就只是想间接使用lock指令,lock一执行完,整个关于内存前面的东西都要执行完刷到内存里,后面的东西才能进行执行。
sync底层实现是 lock comxchg, 因为sync需要自旋获得一把锁才能向下执行。

CPU层面:Intel -> 原语(mfence lfence sfence) 或者锁总线
JVM层级:8个hanppens-before原则 4个内存屏障 (LL LS SL SS)
as-if-serial : 不管硬件什么顺序,单线程执行的结果不变,看上去像是serial

CPU合并写技术

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

cpu在L1之间还有个Load Buffer ,Store Buffer,比L1更快,更小。 在向L1写之前会先写入到这里,不过这个不是所有CPU都有。
并且在L1之间还有个Write Combining Buffer,一般是4个字节
由于ALU速度太快,所以在写入L1的同时,写入一个WC Buffer,满了之后,再直接更新到L2。

UMA(Uniform Memory Access)

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

一块内存,所有CPU共享访问。

NUMA(Non Uniform Memory Access)

JAVA程序员需要知道的计算机底层基础02-CPU组成原理

主板上分为不同的插槽,一个插槽放一组的CPU,这个CPU插槽组附近都有离自己组很近的内存,访问起来比起其他位置的CPU组来说要更快,具有优先级,平时各自访问自己的内存就行了,如果需要别的组的内存需要时再去别人槽附近的内存访问就行了,不过速度略慢一些,因此现在的程序其实做了优化,做到了NUMA aware ,分配内存会优先分配该线程所在CPU的最近内存。例如ZGC当前CPU分配了一个对象,会优先将对象分配在就近的内存里。