jvm执行流程

对于Java程序而言,每个线程在执行时,都有一个PC计数器与一个Java栈。PC计数器以字节为单位记录当前运行位置距离方法开头的偏移量,它的作用类似于ARM架构CPU的PC寄存器与x86架构CPU的IP寄存器,不同的是PC计数器只对当前方法有效,Java虚拟机通过他的值来取指令执行。Java栈用于记录Java方法调用的“活动记录”,Java栈以帧为单位保存线程运行的状态,每调用一个方法,就会分配一个新的栈帧压入Java栈上,每从一个方法返回,则弹出并撤销相应的栈帧,每个栈帧包括局部变量区,求值栈和其他一些信息,局部变量区用于存储方法的参数和局部变量,其中参数按源码中从左到右的顺序保存在局部变量开头的几个slot中。求值栈用于保存求值的中间结果和调用别的方法的参数等


我们以一个demo为例,来看jvm执行的一个基本流程

/**
 * Created by leaves on 2016/11/8.
 */
public class Test2222 {
    public static final int mStatic = 1111111111;
    public static int mStatic2 = 222222222;
    public String minstance = "asdf";
    public int anum = 1;


    public static void main(String[] args) {
      int a=11;
      int b=20;
      int sum;
      int c=2;
       sum =a+b;
      Test2222 test2= new Test2222();
      sum = test2.add(a,b);
    }

	public int add(int a ,int b){
		int sum =10;
		sum=a+b;
		return sum;
	}
}


通过javap -verbose .\Test2222 来反编译class文件,这里主要看main代码段

public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=6, Args_size=1
   0:   bipush  11
   2:   istore_1
   3:   bipush  20
   5:   istore_2
   6:   iconst_2
   7:   istore  4
   9:   iload_1
   10:  iload_2
   11:  iadd
   12:  istore_3
   13:  new     #5; //class Test2222
   16:  dup
   17:  invokespecial   #6; //Method "<init>":()V
   20:  astore  5
   22:  aload   5
   24:  iload_1
   25:  iload_2
   26:  invokevirtual   #7; //Method add:(II)I
   29:  istore_3
   30:  return

这里我们看main函数

 Stack=3表示栈的大小为3, Locals=6表示本地变量占用6个单元(字节), Args_size=1表示一个参数

初始时:


第一条指令bipush

将一个byte型常量值推送至栈顶


第二条指令istore_1

可以分为两部分,第一部分istore表示jvm指令集中的store指令,i是前缀,表示 操作类型为int型,store表示将栈顶指定的数值存储到局部变量,第二部分下划线右边的数字,表示具体要操作哪个局部变量,这里数字 1表示第二个局部变量


第三条bipush  20 第四条istore_2 同理


第4条指令iconst_2 int型常量值2进栈


第5条指令istore  4  将栈顶int型数值存入指定的局部变量4


第 6 7 条指令iload_1  iload_2 分别将局部变量1 2 位置的值入栈



第8条指令add 栈顶两int型数值相加,并且结果进栈


第9条指令istore_3 将栈顶int型数值存入3位置局部变量

第10条指令new 创建一个对象,并且其引用进栈,后面的#5表示类的类型是常量池中5所指向的类型,这里是Test2222


第11条指令invokespecial   #6 调用实例初始化方法 这里是Test2222."<init>":()V

我们知道这个方法没有返回值,当方法调用回来后

第12条指令astore  5 

 将栈顶数值(objectref)存入局部变量数组中指定下标


第13条指令 aload   5 

当前frame的局部变量数组中下标为  5的引用型局部变量进栈

第 14 15条指令同样是入栈指令


第16条指令invokevirtual   #7 调用实例方法 这里是const #7 = Method       #5.#36; //  Test2222.add:(II)I

这里应该是把当前的栈中的值传递给新的函数,而自己本身的栈会清空,因为我们看到第17条指令istore_3将栈顶int型数值存入第四个局部变量,这个栈顶是函数返回过来的,而我们调用函数之前如果不情况当前栈,栈已经满了,放不下数据了,我们可以在源文件中 sum = test2.add(a,b);后面再加几条代码看看,如果里面有进栈操作,则说明应该是调用函数的时候清空了当前栈(纯属猜测)

我们看下add函数

public int add(int, int);
  Code:
   Stack=2, Locals=4, Args_size=3
   0:   bipush  10
   2:   istore_3
   3:   iload_1
   4:   iload_2
   5:   iadd
   6:   istore_3
   7:   iload_3
   8:   ireturn

这里add函数最后ireturn没有画上去,后面会不上,main函数里面最后的return指令也是一样的

bipush 将一个byte型常量值推送至栈顶

istore_3 将栈顶int型数值存入第四个局部变量


iload_1 iload_2将两个局部变量入栈


iadd 栈顶两int型数值相加,并且结果进栈


istore_3 iload_3先将栈顶的int存入局部变量索引为3出,再将局部变量索引为3出的变量入栈


ireturn将当前栈顶int返回

随后add函数的栈帧将撤销


istore_3
将栈顶int型数值存入索引为3的局部变量(sum)

最后return表示当前方法返回void


这样我们就了解了jvm整个的一个执行流程


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值