JVM连载(2)

JMM Java内存模型:

1、 Java的并发采用“共享内存”模型,线程之间通过读写内存的公共状态进行通讯。多个线程之间是不能通过直接传递数据交互的,它们之间交互只能通过共享变量实现。
2、 主要目的是定义程序中各个变量的访问规则。
3、 Java内存模型规定所有变量都存储在主内存中,每个线程还有自己的工作内存。
(1) 线程的工作内存中保存了被该线程使用到的变量的拷贝(从主内存中拷贝过来),线程对变量的所有操作都必须在工作内存中执行,而不能直接访问主内存中的变量。
(2) 不同线程之间无法直接访问对方工作内存的变量,线程间变量值的传递都要通过主内存来完成。
(3) 主内存主要对应Java堆中实例数据部分。工作内存对应于虚拟机栈中部分区域。

1532709451-5ad313f437a51.png

4、Java线程之间的通信由内存模型JMM(Java Memory Model)控制。
(1)JMM决定一个线程对变量的写入何时对另一个线程可见。
(2)线程之间共享变量存储在主内存中
(3)每个线程有一个私有的本地内存,里面存储了读/写共享变量的副本。
(4)JMM通过控制每个线程的本地内存之间的交互,来为程序员提供内存可见性保证。
5、可见性、有序性:
(1)当一个共享变量在多个本地内存中有副本时,如果一个本地内存修改了该变量的副本,其他变量应该能够看到修改后的值,此为可见性。
(2)保证线程的有序执行,这个为有序性。(保证线程安全)
6、内存间交互操作:
(1)lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
(2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
(3)read(读取):作用于主内存变量,把主内存的一个变量读取到工作内存中。
(4)load(载入):作用于工作内存,把read操作读取到工作内存的变量载入到工作内存的变量副本中
(5)use(使用):作用于工作内存的变量,把工作内存中的变量值传递给一个执行引擎。
(6)assign(赋值):作用于工作内存的变量。把执行引擎接收到的值赋值给工作内存的变量。
(7)store(存储):把工作内存的变量的值传递给主内存
(8)write(写入):把store操作的值入到主内存的变量中
6.1、注意:
(1)不允许read、load、store、write操作之一单独出现
(2)不允许一个线程丢弃assgin操作
(3)不允许一个线程不经过assgin操作,就把工作内存中的值同步到主内存中
(4)一个新的变量只能在主内存中生成
(5)一个变量同一时刻只允许一条线程对其进行lock操作。但lock操作可以被同一条线程执行多次,只有执行相同次数的unlock操作,变量才会解锁
(6)如果对一个变量进行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或者assgin操作初始化变量的值。
(7)如果一个变量没有被锁定,不允许对其执行unlock操作,也不允许unlock一个被其他线程锁定的变量
(8)对一个变量执行unlock操作之前,需要将该变量同步回主内存中

堆的内存划分:

2948191696-5ad31617eb2bd.png

Java堆的内存划分如图所示,分别为年轻代、Old Memory(老年代)、Perm(永久代)。其中在Jdk1.8中,永久代被移除,使用MetaSpace代替。
1、新生代:
(1)使用复制清除算法(Copinng算法),原因是年轻代每次GC都要回收大部分对象。新生代里面分成一份较大的Eden空间和两份较小的Survivor空间。每次只使用Eden和其中一块Survivor空间,然后垃圾回收的时候,把存活对象放到未使用的Survivor(划分出from、to)空间中,清空Eden和刚才使用过的Survivor空间。
(2)分为Eden、Survivor From、Survivor To,比例默认为8:1:1
(3)内存不足时发生Minor GC
2、老年代:
(1)采用标记-整理算法(mark-compact),原因是老年代每次GC只会回收少部分对象。
3、Perm:用来存储类的元数据,也就是方法区。
(1)Perm的废除:在jdk1.8中,Perm被替换成MetaSpace,MetaSpace存放在本地内存中。原因是永久代进场内存不够用,或者发生内存泄漏。
(2)MetaSpace(元空间):元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
4、堆内存的划分在JVM里面的示意图:


2866120826-5ad3196c98b32.png

你可能感兴趣的:(JVM连载(2))