前文回顾
A:hello_test.class由类装载子系统加载到方法区(元空间)
B:由字节码执行引擎到方法区(元空间)执行代码。程序计数器是用来记录程序执行的位置,所以字节码执行引擎每执行完一行代码,修改一下程序计数器。
C:根据代码中线程开辟线程空间。由于hello_test.class中只有main线程,所以这里只开辟了main线程空间。
D:而在线程中用到的变量需要存储在栈中,所以线程空间中需要栈。
E:当程序中有用到本地方法时,则线程空间也需要本地方法栈。
由上不难看出,栈、本地方法栈、程序计数器都为每个线程私有,也就是一个线程会为它单独分配这三块。
而堆、方法区(元空间)则为所有线程共用的。这也造成多线程不安全。
堆
堆:存放对象
A:当需要存放对象时,一般存放Eden元区。
B:当Eden元区放满,新对象还再向Eden元区存放时,字节码执行引擎就会在后台开启一个垃圾回收线程(minor gc)。可达性分析算法,通过gc root查询是否有引用。从gc root出发找到了引用则标记为非垃圾数据。
C:将Eden元区中标记的非垃圾对象复制到survivor 1区,并把这些非垃圾对象的分代年龄加一,最后把Eden元区清空。
D:当Eden元区再次放满后,启动minor gc,对Eden元区和survivor 1区中的对象进行可达性分析算法,标记出非垃圾对象,再将这些非垃圾对象复制到survivor 2区,并把这些非垃圾对象的分代年龄加一,最后把Eden元区和survivor 1区清空。
E:minor gc会对Eden元区和非空的survivor 区中对象进行可达性分析算法,最终将非垃圾对象复制到空的survivor 区,并将非垃圾对象的分代年龄加一,如此循环。
F:当有对象的分代年龄超过15,则将该对象移动到老年代。
G:当老年代放满,触发full gc。OOM(内存溢出)
JVM调优:尽量减少gc次数,执行时间。特别是减少full gc次数及执行时间
STW:gc触发STW(stop the Word)停止用户的所有线程,页面形成卡顿。
这里介绍两个java诊断工具
1.Arthas 是Alibaba开源的Java诊断工具
https://arthas.aliyun.com/doc/
2.jdk自带的jvisualvm
以jdk自带的jvisualvm的工具进行分析