jvm内存模型
通过一个简单示例来演示jvm类加载器将字节码加载到内存,jvm执行引擎执行字节码如何分配和利用jvm内存模型的过程
public class Test {
private User user = new User();
public int add() {
int a = 10;
int b = 10;
int c = a + b;
return c;
}
public static void main(String[] args) {
Test test = new Test();
int sum = test.add();
System.out.println(sum);
}
}
在启动jvm执行main方法,首先Test类会被java编译器编译成class字节码文件,类加载器会加载Test.class字节码文件,这个过程是比较复杂的,里面会经过很多步骤比如校验,准备,解析等。最终Test.class字节码文件会以Class对象的形式被加载方法区(元空间)。在一个庞大的web系统里,类加载器并不会一开始就会将所有类直接加载方法区,而是根据所需要的类进行懒加载。
jvm执行main()方法流程:
在执行main()方法之前,会开辟一块栈内存空间供main线程执行时所需,接下来执行创建test对象字节码,创建test对象之前会判断Test类是否被加载过了,如果没有则去加载,否则直接进行创建对象的过程。test对象创建完成后放在堆内存中的某一块内存上,这块内存地址位置就是main线程栈中main方法栈帧局部变量表中的test引用的值。main线程调用test对象的add()方法,在main线程栈中开辟一块add方法栈帧空间用于执行add方法,栈帧包含局部变量表,操作数栈,动态连接和方法出口等,通过jclasslib查看add()方法的字节码文件
0 bipush 10
2 istore_1
3 bipush 10
5 istore_2
6 iload_1
7 iload_2
8 iadd
9 istore_3
10 iload_3
11 ireturn
bipush将一个带符号的8位整型变量压入操作数栈,这里就是将10压入操作数栈,istore_1则是将int类型值存入局部变量表1,3和5的步骤跟0和2的一样,iload_1和iload_2是从局部变量表中装载a和b的值(即10)到操作数栈,然后执行iadd操作(int类型的加法),istore_3将执行的结果存入局部变量表,iload_3从局部变量3中取出值,ireturn返回结果,并从方法出口中退出,销毁栈帧,回到main方法栈帧,最后main方法执行完成,main线程栈内存销毁回收。
jvm内存参数说明
java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar xxx.jar
-Xms:设置堆内存初始化最小的内存
-Xmx:设置堆内存最大的内存大小
它们设置为一样大,可以防止如果-Xms设置的比较小,导致full gc时会重新拓展堆内存(但是不最大不超过-Xmx),如果堆内存使用达到-Xmx设置的值,就会导致OOM。
-Xmn:新生代内存大小。
-Xss:单个线程分配的栈大小
‐XX:MetaspaceSize=256M:元空间最大值,默认是-1,表示不限制
‐XX:MaxMetaspaceSize=256M:元空间初始大小,默认21M,当元空间使用达到21M会触发full gc,并且收集器会对该值进行调整。full gc是会影响性能的,如果在启动的时候频繁full gc,通常是该值设置比较小导致的。所以建议将他们设置的一样大比较合适。