Java 底层基础笔记(一)硬件
计算机组成分为五个部分:运算器,控制器,存储器,输入设备,输出设备。其中,运算器和控制器就是我们常说的 CPU,存储器分为内存储器(内存)和外存储器(硬盘),输入输出设备通称 I/O 设备。
一、一些概念
CPU:计算机的核心,用于计算和控制
内存:用来临时存储 CPU 需要的数据,也是 I/O 设备与 CPU 进行沟通的桥梁
总线:由一堆用来传输信息的导线组成。通常分为数据总线、地址总线、控制总线,分别用来传输数据、地址和命令。
四核八线程:一颗 CPU(一般笔记本电脑只有一颗 CPU)有 4 个物理核,每个核有 2 个逻辑核。每个物理核有自己独立的 ALU、PC、Registers、L1、L2,两个物理核通过 L3 通信,两个逻辑核通过 L2 通信
超线程:一个 ALU 对应多个 PC | Registers,所谓的四核八线程
二、CPU
CPU 和 内存是计算机中最核心的部分。CPU 的基本组成一般有 CU、ALU、Registers、PC、MMU、Cache 等。其中:
CU:Control Unit,控制单元
ALU:Arithmetic & Logic Unit,逻辑运算单元
Registers:寄存器,用来暂时存储 CPU 计算需要用到的数据
PC:Program Counter,程序计数器,用来记录当前指令地址,可以把内存看作一个超大的字节数组,PC 存储着该指令在这个字节数组的下标
MMU:Memory Management Unit,内存管理单元
Cache:缓存
1、CPU 组成图
多核 CPU 如下所示,多个 CPU 通过总线通信,单颗 CPU 的多个核心通过 L3 通信,单个核心的多个逻辑核心通过 L2 通信(超线程技术)。
2、Cache
CPU Cache 目前通常使用三层缓存,L1、L2、L3。增加缓存的目的是为了提高效率,因为主存的速度差不多要比寄存器慢 100 倍,具体的存储器层次结构如下图:
3、程序局部性原理
CPU 为了提高效率,充分发挥总线、CPU 针脚等一次性读取更多的数据,通常会按块读取数据,也就是一次性读取一个 cache line(缓存行)的数据,现代 CPU 缓存行大小通常为 64 个字节。
MESI 缓存一致性协议:保证同一缓存行数据的一致性,是缓存锁的实现之一。但当跨多个缓存行或无法被缓存的数据,则必须使用总线锁
Java 中的缓存行对齐
对于有些特别敏感的数字,会存在线程高竞争的访问,为了保证不发生伪共享,可以使用缓存行对齐的编程方式,如 disruptor
JVM 会对 Java 对象和对象内的字段做内存对齐,必须是 8 的倍数。这是为了让对象或字段只出现在同一 CPU 的缓存行中,防止跨缓存行存储污染多个缓存行
局部性:除了 CPU 会分 cache line,内存也会分页,硬盘分块,都是为了提高效率。
4、CPU 乱序执行
为了提高效率,CPU 会在进行读等待的同时执行先执行指令,从存储器的层次结构我们知道,主存比寄存器约慢了 100 倍,如果 CPU 执行一条指令需要等待去主存读取数据后才能再执行,那么会浪费 CPU 的资源,所以当前后两条执行没有关联关系时,CPU 会先执行下一条指令。这在单线程下是没有问题的,但当跑在多线程环境时,就会出现指令重排序的乱序问题。
(1)CPU 层面如何禁止重排序?
通过内存屏障,Intel 提供三条原语(mfence lfence sfence)实现内存屏障
锁总线
Intel 的 lock 汇编指令
(2)JSR 内存屏障(JVM)
JVM 提供四条内存屏障指令,分别是:LoadLoad、StoreStore、LoadStore、StoreLoad。如 volatile 就是通过这四条内存屏障实现禁止指令重排。但在 CPU 层面是通过 lock addl 指令实现
(3)Hanppens-before 原则
JVM 规定重排序必须遵守的 8 个规则。在这 8 中规则之内不允许指令重排。
(4)as-if-serial 语义
不管如何重排序,单线程执行结果不会改变(看上去像是 serial 一样)。
5、编译执行与解释执行
编译执行:编译好的编译文件,已经是机器码了,如 C 语音编译文件。读取到内存中,CPU 可以直接运行。
解释执行:编译好的文件,是一个字节码(bytecode)文件,如 Java 的 class 文件,需要 JVM 解释成机器码,再交给 CPU 执行。
版权声明: 本文为 InfoQ 作者【奈何花开】的原创文章。
原文链接:【http://xie.infoq.cn/article/7d9a6333cd53c135b6653f372】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论