一、CPU和内存
说起内存要从cpu和内存的关系说起,由于cpu发展太过迅速,内存读写速度无法跟上cpu的处理速度,于是amd等厂商为每颗cpu每个核加上了一块高速缓冲区,也就是我们常见的cpu L1、L2、L3级缓存。这样做就解决了cpu和物理内存的读写速度差。就像当年用物理内存来解决cpu和物理磁盘的速度差一样。
而CPU又是整台主机中最为贵重的模块,成本最高,缓存也设计的比较小,如博主这台macbook pro,L2仅为256KB,L3也仅仅4MB。当我们使用计算机处理数据的时候,CPU会优先由L1、L2、L3级缓存依次查找,如果都没有找到才会去读取物理主存。
谈到CPU缓存那么就诞生了一个问题,多核心CPU的缓存一致性问题,如果两个核心同时去修改主存中的一个变量“a”,这个时候1线程和2线程同时读取到变量a做修改,按如下图的逻辑,无论线程1先完成还是线程2先完成都会导致缓存主存中的结果不是我们想要的到的最终结果,我们想要的是主存中的变量“a”的值为“100+10+20=130”这个结果。
那么我们如何解决缓存一致性的问题呢,那就要用到MESI等协议来保证了,我们以MESI为例,MESI就是Modified Exclusive Shared Or Invalid,其实就是标记了缓存中的数据的几个状态:被修改、独享的、共享的、无效的。以上面变量“a”为例,这个协议就是保证了假设线程1优先给主存变量“a”赋值,这个时候就会给其他核心的缓存变量打上“Invalid”的标记,当线程2检测到这个标记为“I”的时候,就会从主存中重新读取变量“a”来进行处理,并最终写回主存。
二、JVM
jvm区域总体分两类,heap区和非heap区。
1、heap区
heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)。
1、JVM实质上分为三大块,年轻代(YoungGen),年老代(Old Memory),及持久代(Perm),持久代在Java8中已经被去掉,我们可以不做详细描述。
2、GC(Garbage Collection),分为2种。第一种就是我们所说的YoungGC(Minor GC),即对年轻代的垃圾回收,主要针对的是对伊甸园区我们new出来且没有引用的对象进行内存释放。第二种就是我们所说的FullGC(Major GC),他会针对老年代区域进行索引扫描,找到之前YoungGC放置过来的对象且当前没有引用的,并将其释放掉。
3、年轻代包含三个区域,分别为eden、survivor0、survivor1。当我们new一个对象的时候,首先这个对象会被放置到eden区域。当发生Young GC后没有被引用的就回被垃圾回收器收回内存,如果还存在引用的变量就会被放置到Survivor区域(幸存者区,分为两块,每次GC后只有一个S区是有数据的,另一个S区为空,且这两个空间大小一致,目的是方便每次GC的时候赋值)。当我们发生了多次Young GC以后,也就是这个对象在S0区、S1区多次辗转反复后还没有被清除,这个时候这个对象就会被放置到老年代。
4、需要注意任何形式的GC操作都会导致整个程序中断(stop the world)一些事物的处理,如果GC时间过长也会导致一系列的程序问题,或者连接超时问题。
5、从JVM调优的角度来看,我们应该尽量避免发生YGC或FullGC,或者使得YGC和FullGC的时间足够的短。
2、非heap区
非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈)。