1.操作系统内存与JVM内存
文章目录
操作系统内存与JVM内存模型
- 硬件内存模型: 处理器->高速缓存->缓存一致性协议->主存
寄存器的价值
1. 距离
寄存器在CPU内部.
距离不是主要因素,可是最好懂,所以放在最前面说。内存离CPU比較远。所以要耗费更长的时间读取。
以3GHz的CPU为例,电流每秒钟能够振荡30亿次。每次耗时大约为0.33纳秒。
光在1纳秒的时间内,能够前进30厘米。也就是说。在CPU的一个时钟周期内。光能够前进10厘米。
因此。假设内存距离CPU超过5厘米。就不可能在一个时钟周期内完毕数据的读取。这还没有考虑硬件的限制和电流实际上达不到光速。相比之下,寄存器在CPU内部,当然读起来会快一点。
距离对于桌面电脑影响非常大。对于手机影响就要小得多。手机CPU的时钟频率比較慢(iPhone 5s为1.3GHz)。并且手机的内存紧挨着CPU。
2. 硬件设计不同与内存不同
寄存器的晶体管一直有电,而内存的晶体管仅仅实用到的才有电,没用到的就没电,这样有利于省电。
3. 工作方式不同
寄存器的工作方式非常easy。仅仅有两步:(1)找到相关的位。(2)读取这些位,内存的工作方式就要复杂得多
高速缓存
高速缓存的价值
- CPU的运行效率要远远高于ARM也就是我们的主存,那么在这样的一个大前提下,当CPU所需要的数据需要从ARM中读取时,相对来说CPU有很大一部分性能被闲置,并未发挥到CPU最大效能
- 所以基于前一点,加入高速缓存后,高速缓存的运行效率与CPU接近,锁需要的数据从高速缓存获取,这样一来,尽可能的避免了CPU被闲置的情况,从而从整体上提高了程序的执行效率
高速缓存运作原理
- 高速缓存会将在CPU执行所需要的数据从ARM中拷贝写入到高速缓存中,那么在对高速缓存进程操作的时候会有两种情况:
- 高速缓存被命中 : 在在CPU执行时,对于所需要的数据首先会去与之性能最接近的高速缓冲中寻找,当在高速缓存中找到后就直接执行,那么在这样的情况下就凸显出了高速缓存的价值
- 高速缓存未被命中 : 在在CPU执行时,对于所需要的数据首先会去与之性能最接近的高速缓冲中寻找,当未找到,CPU被迫等待,后续由高速缓存从ARM中拷贝再从高速缓存读取
高速缓存的回收机制
假如高速缓内存空间已满,那么所需要做的就是清理置空一部分高速缓存的内存空间,为后续使用提供空间,最常见的一种策略就是LRU原则(最近最少使用原则),该原则在java的LinkedHashMap就有使用,其底层实现的原理就是LRU原则
高速缓存与命中率
CPU在从高速缓存中读取数据之前,那么就先必须找到所需要的数据它在高速缓存上的位置,而查找又需要时间,由此一来就延伸出了一个问题: 在高速缓存上的寻找效率和命中率
-
直接映射: 高速缓存只提供一块缓存给ARM中提供的数据进行存储,那么CPU只需要直接查找这个位置所需要的数据是否存在就可,但这也存在着一个缺陷就是一个缓存快的容量非常有限,很大程度上限制了ARM所提供的数据量,这样一来,尽管提高了寻找效率但很大程度上降低了命中率
-
关联映射: 与直接映射完全相反, 高速缓存中的任意缓存块都可提供给ARM的数据进行存储,而这样一来,这极大的提高了命中率,但也增加了CPU寻找的时间
-
组相联映射 : 这是上面二者之间比较折中的方案,提供数量有限的缓存块用来存储ARM中提供的数据,例如:2路相联映射系统将ARM中提供的数据存储到两个中的任一一个缓存块中,8路相联映射系统就是将ARM中提供的数据存储到8个中的任一一个缓存块中进行存储
缓存的写策略
- "write-through"策略:该策略下,任何写入高速缓存的数据都会直接写入RAM
- “write-back策略”: 任何写进高速缓存的数据都会被标记为"dirty",当该数据从高速缓存中删除时,才会写入RAM刷新RAM中原来的数据,在该策略下会导致多线程中对数据的不可见性
缓存一致性协议
该协议的主要作用就是在多核处理器上,每个核心对于共享数据保证数据一致性的处理解决方案,由于篇幅过长,推荐大家可看此处: 缓存一致性协议
多线程与多核
-
多核心处理器是指:在一个处理器上集成多个运算处理核心,从而提高计算能力,也就
有多个真正具有并行计算的核心,每一个处理核心,对应一个内核
线程. -
内核线程:就是直接由操作系统内核支持的线程,内核线程是由内核来完成
切换的,而内核通过操作调度器对线程完成调度,内核并且负责
将线程的任务映射到各个处理器上.
一般来说,一个内核对应一条内核线程,但现在一般的CPU,都是
一个处理核心对应两条内核线程,其所采用的是超级线程技术,
将一个物理处理核心虚拟成两个逻辑处理核心,来实现让单个处理核心有也具有线程并行运算能力.
程序一般不会直接去使用内核线程,而是去使用内核线程的高级
接口—轻量级进程,也就是我们常说的 用户线程,每一个用户
线程都会有一个内核线程,所以需要先对内核线程支持,才会有
Jvm、Dalvik和Art的区别
Java内存模型
规范了线程如何和何时可以看到其他线程修改过的共享变量的值,以及在必须时如何同步的访问共享变量
注:图2引用于百度图库
工作内存
工作内存是java模型中的一个抽象概念,用来指定代表CPU的寄存器和高速缓存等
本地内存及其私有化性质
- 同工作内存一样是一个抽象描述
- 从图中我们可以看到,在线程执行时,会对数据所需要的数据进行私有拷贝作为副本
- 所需要注意:当两个线程中的两个方法同时指向堆内存中的同一个成员变量的时候,这时两个线程都能够对该成员变量进行 私有拷贝作为副本,各线程在执行的都可以对该成员变量的副本进行操作,而这过程中,各线程中该副本的值可能发生变化,这是导致多线程安全问题的本质
- 由上图2可以到,调用栈和本地变量都是存放线程栈中,执行过程中所调用的的方法也放在线程栈中
java内存中的交互操作
注:图引用于百度图库
java内存中数据同步规则
- lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
- unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
- write(写入):作用于主内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中
确保并发操作安全的八条原则
- 不允许read和load,store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现.
- 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存.
- 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存.
- 一个新的变量只能在主内存中"诞生",不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说,就是对一个变量实施use,store操作之前,必须先执行过了assign和load操作.
- 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁.
- 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值.这就使得线程加锁时使用共享变量时需要从主内存中重新读取最新的值
- 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量.
- 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store,write操作).