认识Java底层操作系统与并发基础
一、现代计算机硬件结构
核心部分: CPU、内存
1.CPU内部结构
- 控制单元: 整个 CPU 的指挥控制中心
- 运算单元: 运算器核心,执行算术运算与逻辑运算。运算器接收控制单元的指令而执行动作
- 存储单元: CPU 中暂时存储数据的地方,包括 CPU 片内缓存 Cache 和 寄存器组
1.1.CPU缓存结构
现代 CPU 为了提升执行效率,减少 CPU 与内存的交互(交互影响 CPU 效率),一般在 CPU上集成了多级缓存架构,常见的为三级缓存结构
- L1 Cache,分为数据缓存和指令缓存,逻辑核独占
- L2 Cache,物理核独占,逻辑核共享
- L3 Cache,所有物理核共享
此机器的三级缓存架构如下图:L1 Cache又分为两种,指令存储单元(存指令),和逻辑存储单元(存逻辑)。理论上一台机器可以有多个 CPU,由插槽决定,一个 CPU 又有多核,一个核又可以由多个逻辑处理器。
寄存器是 CPU 内部元件,读写速度非常快。 CPU 读取数据只会从寄存器中去取,每个 CPU 都有一个独有的寄存器,其他 CPU 无法访问。采用寄存器,可以减少 CPU 访问内存的次数,从而提高了 CPU 的工作速度。
越靠近 CPU 读取速度越快,摩尔定律中,CPU 以每18个月翻一番的速度在发展,而内存和硬盘的发展速度远远跟不上。为了解决 CPU 运算速度和 I\O 速度不匹配的问题,CPU 开始被内置了少量的高速缓存 Lx Cache(CPU空间有限,存储元件大小受限)。
- 存储器存储空间大小: 内存 > L3 Cache > L2 Cache > L1 Cache > 寄存器
- 存储器读取速度快慢: 寄存器 > L1 Cache > L2 Cache > L3 Cache > 内存
- 缓存是由最小的存储区块--- 缓存行(CacheLine) 组成,缓存行大小通常为64byte。我的机器L1的缓存大小时512K,则由512 * 1024/64个缓存行组成。
CPU读取存储器数据过程: CPU 仅能直接从寄存器中获取数据。 假设数据 x = 0 在内存中,则它的取值过程如下:
判断寄存器中是否存在
不存在则遍历L1 Cache 看是否存在,不存在遍历L2 Cache,L2 Cache 中没有,遍历L3 Cache。中间过程存在,则会把 Cache 行锁住,拷贝到上一级,直至到寄存器。
Cache 中没有则区内存中找,先通知内存控制器占用总线带宽,通知内存加锁,发起内存读请求,等待回应,回应数据拷贝到L3 Cache。 注意:整个过程加锁直至到CPU才会解开
局部性原理:在CPU访问存储设备时,无论是存取数据还是存取指令,都趋于聚集在一片连续的区域中。
这种局部性原理又有两种:
- 时间局部性(Temporal Locality): 如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。 比如循环、递归、方法的反复调用等。
- 空间局部性(Spatial Locality): 如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。 比如顺序执行的代码、连续创建的两个对象、数组等。
空间局部性的例子: 一个很大的二维数组,累加求和一行一行加会比一列一列累加快很多。在CPU 在内存中读取数据时会将附件的数据都读进去。
1.2.CPU运行安全等级
CPU被划分为 4 个运行级别:
- ring0 内核态
- ring1
- ring2
- ring3 用户态
linux 和 windows 都只用到了两个级别:ring0、ring3,操作系统内部内部程序指令通常运行在 ring0 级别,操作系统以外的第三方程序运行在 ring3 级别,第三方程序如果要调用操作系统内部函数功能,由于运行安全级别不够,必须切换CPU运行状态,从 ring3 切换到 ring0, 然后执行系统函数,创建线程,线程阻塞唤醒是重型操作,因为CPU要切换运行状态。
JVM 创建线程是 CPU 的流程:
- 第一步:CPU 从 ring3 切换 ring0 创建线程
- 第二步: 创建完毕,CPU从 ring0 切回 ring3
- 第三步: 线程执行JVM程序
- 第四步: 线程执行完毕,销毁切回 ring0
- 第五步: 线程销毁,切回 ring3
2.操作系统内存管理
为了使程序运行安全隔离与稳定,操作系统有用户空间
与内核空间
两个概念。以 32位操作系统4G大小的内存空间为例:
Linux 为内核代码和数据结构预留了几个页框,这些页永远不会被转出到磁盘上(4GB内存空间,用户程序可使用3GB)。如图绿色部分的线性地址可由用户代码和内核代码进行引用(即用户空间)。黄色部分的线性地址只能由内核代码进行访问(即内核空间)。
进程与线程只能运行在用户方式(usermode) 或 内核方式(kernelmode) 下。用户程序运行在用户方式下,而系统调用运行在内核方式下。
用户方式下使用一般的堆栈(用户空间的堆栈),内核方式下使用固定大小的堆栈(内核空间的堆栈,一般为一个内存页的大小),即每个进程与线程其实有两个堆栈,分别运行与用户态与内核态。
CPU调度的基本单位线程,也划分为:
- 内核线程模型(KLT): Java使用,内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。
- 用户线程模型(ULT): 不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/内核态切换,速度快。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞
线程都有两个堆栈,一个在用户空间,一个在内核空间。阻塞、创建、杀死线程将抛弃用户空间的堆栈,转移到内核空间,执行完毕后再转移到用户空间。
3.进程与线程
进程: 操作系统资源分配的最小单位,例如:启动一个 Java 程序,操作系统就会创建一个Java 进程,进程中可以包含多个线程。
线程: 操作系统调度CPU的最小单元,线程都拥有各自的计数器、堆栈和局部变量等属性, 并且能够访问共享的内存变量。CPU 在这些线程上高速切换,让使用者感觉到这些线程在同时执行(并发)。
线程上下切换: 保存上一个线程运行的中间状态,执行下一个线程
- 串行: 时间上不可重叠,前一个任务没完成,下一个任务只能等待
- 并行: 时间上是重叠的,两个任务在同一时刻互不干扰的同时执行
- 并发: 运行两个任务彼此干扰,同一时间点,只有一个任务执行,交替执行
到此这篇关于认识Java底层操作系统与并发基础的文章就介绍到这了,更多相关Java底层操作系统内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
相关文章