JVM内存模型
说明:
1、JVM由装载子系统、运行时数据区(jvm内存模型)、字节码执行引擎;
2、运行时数据区包含堆、元空间、栈、本地方法栈和程序计数器;
3、堆、元空间是线程共享;方法栈、程序计数器是线程独有,每个线程都会有;
4、运行java Calculate.class之后,先由类装载子系统,将字节码文件加载到运行时数据区;
5、栈帧:一个方法对应栈帧,每调用一个方法,会为该方法生成一个栈帧压入方法栈,方法执行完成栈帧弹出。在弹出栈帧之前根据方法出口找到要返回的调用方法的位置;
6、栈帧主要包含:局部变量表、操作数栈、动态链接、方法出口;
7、栈和堆的关系:如果局部变量是对象类型,堆中存放对象的具体内容、栈(的局部变量表)存放内容的引用(地址);
8、方法区和堆的关系:方法区的静态变量是对象的引用(对象在对象的地址);堆存对象的具体的内容;
JVM内存操作过程
通过一个简短的代码说明在jvm内存操作过程:
package com.example.demo;
public class Calculate {
public int compute(){int a=5;int b=7;
return (a+b)*9;}
public static void main(String[] args) {Calculate calculate = new Calculate();
System.out.println(calculate.compute());}
}
使用javap -v Calculate.class
命令反编译以上类的字节码文件,如下(部分):
{public com.example.demo.Calculate();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 8: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lcom/example/demo/Calculate;
public int compute();descriptor: ()Iflags: (0x0001) ACC_PUBLICCode:stack=2, locals=3, args_size=10: iconst_51: istore_12: bipush 74: istore_25: iload_16: iload_27: iadd8: bipush 910: imul11: ireturnLineNumberTable:line 12: 0line 13: 2line 15: 5LocalVariableTable:Start Length Slot Name Signature0 12 0 this Lcom/example/demo/Calculate;2 10 1 a I5 7 2 b I
public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: new #2 // class com/example/demo/Calculate3: dup4: invokespecial #3 // Method "<init>":()V7: astore_18: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;11: aload_112: invokevirtual #5 // Method compute:()I15: invokevirtual #6 // Method java/io/PrintStream.println:(I)V18: returnLineNumberTable:line 19: 0line 21: 8line 22: 18LocalVariableTable:Start Length Slot Name Signature0 19 0 args [Ljava/lang/String;8 11 1 calculate Lcom/example/demo/Calculate;MethodParameters:Name Flagsargs
}
SourceFile: "Calculate.java"
分析compute()方法的汇编指令执行:
分析compute()方法的汇编指令执行:
public int compute(); descriptor: ()I flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: iconst_5 //将int类型常量5压入栈
1: istore_1 //将int类型值存入局部变量1
2: bipush 7 //将一个8位带符号整数压入栈
4: istore_2 //将int类型值存入局部变量2
5: iload_1 //从局部变量1中装载int类型值
6: iload_2 //从局部变量2中装载int类型值
7: iadd //执行int类型的加法
8: bipush 9 //将一个8位带符号整数压入栈
10: imul //执行int类型的乘法
11: ireturn //从方法中返回int类型的数据
执行流程:
1、将常量5压入操作数栈;
2、将操作数栈顶的值5存入局部变量表1号位置;
3、将整数7压入操作数栈;
4、将操作数栈顶的值7存入局部变量表2号位置;
5、从局部变量1号位置取数压入操作数栈;
6、从局部变量2号位置取数压入操作数栈;
7、将操作数栈顶的两个数相加,并压入栈顶,此时操作数栈只有一个数是5+7=12;
8、将整数9压入操作数栈;
9、将操作数栈顶的两个数相乘,并压入栈顶,此时操作数栈只有一个数是12*9=108;
10、返回计算结果,compute()方法执行完成;
之后,根据compute()方法对应的栈帧的方法出口获得应该返回的main方法的地址,该栈帧从方法栈中弹出,程序回到main方法中继续执行。