1、GC调优步骤
打印GC日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log
1、因为Minor GC执行时间非常短对性能影响不大,当MinorGC执行次数非常多且密集,也是需要分析。
2、正常FullGC间隔几十s或几分钟。此时仅仅间隔5s则不正常。
3、元空间:在程序刚加载时会从0增长,因为需加载各种jar包 而在程序正常执行时空间不变。
此时增大元空间大小。
2、Java线程内存模型--并发编程JMM
a、CPU多核并发缓存架构
b、Java线程内存模型底层实现原理
c、CPU缓存一致性协议详解
d、深入汇编语言底层原理理解volatile
e、并发编程可见性,原子性与有序性
1、多核并发缓存架构
CPU缓存实际在CPU内部,数据从磁盘加载到主内存,再从主内存加载到CPU缓存。
2、JMM内存模型
Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程模型是标准化的,屏蔽掉了底层不同计算机的区别。
工作内存存储共享变量的副本,类似于CPU缓存,距离CPU近效率高。
initFlag作为共享变量,每个线程仅仅操作本地副本,不对其他线程可见。导致问题。
public class VolatileVisibilityTest {
private static boolean initFlag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("waiting data;");
while(!initFlag){}
System.out.println("===success====");
}
}).start();
Thread.sleep(2000);
new Thread(new Runnable() {
@Override
public void run() {
prepareData();
}
}).start();
}
public static void prepareData(){
System.out.println("prepareing data...");
initFlag = true;
System.out.println("prepare data end");
}
}
waiting data;;;
prepareing data...
prepare data end
使用volatile解决可见性问题
public class VolatileVisibilityTest {
private static volatile boolean initFlag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("waiting data;");
while(!initFlag){}
System.out.println("===success====");
}
}).start();
Thread.sleep(2000);
new Thread(new Runnable() {
@Override
public void run() {
prepareData();
}
}).start();
}
public static void prepareData(){
System.out.println("prepareing data...");
initFlag = true;
System.out.println("prepare data end");
}
}
waiting data;
prepareing data...
prepare data end
===success====
3、JMM详解
1、早期如何实现内存可见性问题?
使用的总线加锁,性能太低:cpu从主内存读取数据到高速缓存,会在总线对这个数据加锁,这样其他cpu没法去读或写这个数据,直到cpu使用完数据释放锁之后其他cpu才能读取该数据。
即当第一个线程read读取数据之前,先对数据lock,线程执行完成并将最新值更新到主内存后,在unlock。己缓存里的数据失效。
2、现在使用什么方式?
MESI缓存一致性协议:多个CPU从主内存读取同一个数据到各自的高速缓存,当其中某个CPU修改了缓存里的数据,该数据会马上同步回主内存,其他CPU通过总线嗅探机制【类似于监听】可以感知到数据的变化,从而将自己缓存里的数据失效,然后重新从主内存读取到最新的值。
其中:总线是CPU与内存等硬件之间数据交换/通信的桥梁。
3、Volatile缓存可见性实现原理?
底层实现主要通过汇编lock前缀指令,它会3)锁定这块内存区域的缓存(缓存行锁定)并回写到主内存。
lock指令:1)会将当前处理器缓存行的数据立即写回系统主内存;2)这个写回主内存的操作会引起在其他CPU里缓存了该内存地址的数据无效(MESI协议)
4、汇编语言分析
以下是对添加了volatile关键字后使用javap -v Math.class得到的汇编语言。
其中:lock是汇编关键字。 add dword ptr [rsp],Oh是汇编代码。
add dword ptr [rsp],Oh 作用:就是将cpu修改的值assign到工作内存,将值复制给initFlag即上图中线程2的assign操作。
含有lock关键字的作用:cpu硬件会将当前处理器缓存行【cpu的工作内存】的数据立即写回系统主内存【即将 add dword ptr [rsp],Oh操作的值,不管该线程后面是否有其他代码,都会立即先将该值写回主内存】,然后在执行线程后面的代码。
lock三层含义:1、当工作内存的值一旦被修改,cpu硬件会立即将该值同步回主内存。同步回主内存过程中会经过总线,此时会触发缓存一致性协议以及cpu总线嗅探机制,其他线程监听到后通过MESI将本地内存的值置为失效状态。
2、在store之前加lock锁,即锁定这块内存的缓存。当执行unloack解锁后,其他线程才能读到主内存的值。
3、这个写回主内存的操作会引起在其他CPU里缓存了该内存地址的数据无效(MESI协议)
5、并发编程三个特性
可见性、原子性、有序性
volatile保证可见性与有序性,但是不保证原子性,保证原子性需要借助synchronized这样的锁机制。