JVM
内存模型组成部分
堆,栈,本地方法,元空间,程序计数器组成
堆
主要是存放new的对象。堆区主要由年轻代和老年代组成,其中年轻代占堆的1/3,老年代占2/3.
年轻代又由:Edan区,Survivor区(S0区和S1区),看图:
Edan:占年轻代的8/10.
S0:占年轻代的1/10
S1:占年轻代的1/10
堆的运行原理
第一:
首先Edan区存放的new的对象,当Edan区满了后,会触发minor GC时,根据可达性算法 开始查找,将使用的对象都放入到S0,然后清理整个Edan区。
第二:
再次触发minor GC时,Edan区算法不变,存入Survivor方法不变,S0区存活的对象放入S1,并且年龄加1,然后清理整个年轻代,然后再次minor GC时,S1区存活的对象放入S0(标记整理算法),如此反复到一定年龄后(大概时15),存入老年代。
第三:
当老年代的数据满时,会触发一次Full GC清理整个堆,如果full GC清理后,老年代依然是满的,那么就OOM了。
栈
栈:我们可以理解为每一个线程,JVM都会为我们分配一个栈内存,栈内存里面存放栈帧。
栈帧:我们可以理解为,当前线程运行到当前的类 里面的一个方法,每个方法就是一个栈帧。
所以可以看到,栈帧里面还存放当前方法的局部变量的。
局部变量:当前方法的局部变量
操作数栈:
public void test() {
int a = 1 + 2;
int b = 3;
}
比如当前方法有个a = 1 + 2;b=3,那么在这个操作数栈,首先压入栈1,在压入2,在运算,在出栈,在赋值给局部变量区的a,然后这个时候的程序计数器指向b=3这一行的地址。
动态接口:public void test() {},其中这个test()我们称之为符号,而这个test方法的是存在元空间的,所以可以知道这个动态接口存放的就是符合和他在元空间对应的地址。
方法出口:
当程序运行到a.test()时,方法出口指向b.test2()的地址。
public static void main(String args[]) {
a.test();
b.test2();
}
元空间(方法区)
主要存放常量,静态变量,类信息。
private static User user = new User();
此时的User user存放在元空间,而new User则存放在堆空间。
程序计数器
每一个栈都会有一个程序计数器。当一个栈帧运行到某一行时,此时程序计数器指向当前运行的行的下一行。如果发生另一个线程优先级特别高,那么肯定会抢占当前CPU,此时当前程序计数器会记录到当前运行行的下一行地址,当前行会继续执行完,阻塞当前位置,然后优先级高的线程运行完后,根据当前程序计数器记录的位置,继续运行。
本地方法栈
凡是被native修饰的方法都是本地方法