Java底层篇(1.3)
1、JVM
→ Java 内存模型
计算机内存模型、缓存一致性、MESI 协议
计算机内存模型:
之前文章已经总结过。
CPU与缓存一致性
随着CPU的技术发展,CPU执行速度越来越快,但是内存技术并没有太大的变化,导致CPU操作内存都要耗费很多时间。所以在CPU和内存之间增加高速缓存。
那么,当程序在运行过程中,会将运算需要的数据从主存复制一分到CPU的高速缓存中,那么CPU在计算时就可以直接从他的高速缓存中读取和向其中写入数据,当运算结束后,再将高速缓存中的数据刷到主存当中。
而随着CPU能力的不断提升,一层缓存就慢慢的无法满足要求了,就逐渐的衍生出多级缓存。
MESI 协议
多核CPU的情况下有多个一级缓存,如何保证缓存内部数据的一致,不让系统数据混乱。这里就引出了一个一致性的协议MESI。
MESI 是指4种状态的首字母。每个Cache line有4个状态,可用2个bit表示,它们分别是:
|状态|描述|监听任务|
|–|–|–|–|
|M 修改 (Modified)| 该Cache line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。|缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。|
|E 独享、互斥 (Exclusive)|该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。|缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。|
|S 共享 (Shared)|该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中。|缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid)。
|I 无效 (Invalid)|该Cache line无效。|无|
注意:
对于M和E状态而言总是精确的,他们在和该缓存行的真正状态是一致的,而S状态可能是非一致的。如果一个缓存将处于S状态的缓存行作废了,而另一个缓存实际上可能已经独享了该缓存行,但是该缓存却不会将该缓存行升迁为E状态,这是因为其它缓存不会广播他们作废掉该缓存行的通知,同样由于缓存并没有保存该缓存行的copy的数量,因此(即使有这种通知)也没有办法确定自己是否已经独享了该缓存行。
从上面的意义看来E状态是一种投机性的优化:如果一个CPU想修改一个处于S状态的缓存行,总线事务需要将所有该缓存行的copy变成invalid状态,而修改E状态的缓存不需要使用总线事务。
MESI状态转换
【参考链接】https://www.hollischuang.com/archives/2550
https://www.cnblogs.com/yanlong300/p/8986041.html
可见性、原子性、有序性、happens-before、
可见性:多个线程同时访问一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值。
原子性:指原子性的操作是不可中断的,要么全部执行,要么全部不执行。
线程是CPU调度的基本单位,CPU有时间片的概念,根据不同的调度算法调度线程,一个线程获取时间片后开始执行,多线程情况下,容易发生原子性问题。
有序性:有序性即执行程序的顺序按照代码的先后顺序执行。
happens-before:这个原则会保证在发生指令重排情况下,程序还保证有序性,其原则在 Java基础篇(6)中写到过。其规则包含程序次序规则、监视器锁规则、volatile域规则、传递规则、线程规则等。
→ JVM 参数及调优
-Xmx、-Xmn、-Xms、Xss、-XX:SurvivorRatio、
-Xmx:堆内存的最大大小,默认为物理内存的1/4
-Xmn:堆内新生代的大小,通过这个值可以得到老生代的大小:-Xmx减去-Xmn
-Xms:堆内存的最小大小,默认为物理内存的1/64
-Xss:设置每个线程可使用的内存大小,即栈的大小。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。
-XX:NewRatio:设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
-XX:SurvivorRatio:新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:为3,表示Eden:Survivor=3:2,一个Survivor区占整个新生代的1/5
-XX:PermSize、-XX:MaxPermSize、-XX:MaxTenuringThreshold
-XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小(Java8以前)
-XX:MetaspaceSize、-XX:MaxMetaspaceSize:分别设置元空间最小大小与最大大小(Java8以后)
-XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
【参考链接】https://blog.youkuaiyun.com/yrwan95/article/details/82826519