02 字节码原理初步 —— 基于栈的执行引擎

一、栈、寄存器

虚拟机常用实现方式:Stack based(基于栈)和 Register based(基于寄存器)

Stack based

  • HotSpot JVM
  • .net CLR

Register based

  • LuaVM
  • DalvikVM

基于栈的指令集移植性更好,代码更紧凑、编译器实现更简单
基于寄存器的指令集完成相同功能所需的指令数更少,执行速度更快一些

二、栈帧(Stack Frame)

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构

  • 栈帧随着方法调用而创建,随着方法结束而销毁
  • 栈帧存储空间分配在 Java 虚拟机栈中
  • 每个栈帧都有自己的局部变量表(Local Variables)、操作数栈(Operand Stack)和指向运行时常量池的引用

栈帧

局部变量表

  • 每个栈帧内部都包含一组称为局部变量表的变量列表
  • 局部变量表的大小在编译期间就已经确定
  • JVM 使用局部变量表来完成方法调用时的参数传递

当一个方法被调用时,方法对应的参数会被传递到局部变量表中,下标从 0 开始。当实例方法是非静态方法,下标为 0 的局部变量就是 this(实例方法的对象的引用)
局部变量表

操作数栈

  • 每个栈帧内部都包含一个称为操作数栈的栈
  • 栈的大小在编译期间确定

在方法调用时,用来准备调用方法的参数和接收方法返回的结果
局部变量表与操作数栈之间 load、store 过程

三、举个 xue 微复杂的栗子

public class ScoreCalculator {
    public void record(double score) {
        
    }
    public double getAverage() {
        return 0;
    }
    
    public static void main(String[] args) {
        ScoreCalculator calculator = new ScoreCalculator();
        
        int score1 = 1;
        int score2 = 2;
        
        calculator.record(score1);
        calculator.record(score2);
        
        double avg = calculator.getAverage();
    }
}

javap 查看字节码

javap -c -v ScoreCalculator

复制 main() 函数部分,如下

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=6, args_size=1
         0: new           #2                  // class ScoreCalculator
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: iconst_1
         9: istore_2
        10: iconst_2
        11: istore_3
        12: aload_1
        13: iload_2
        14: i2d
        15: invokevirtual #4                  // Method record:(D)V
        18: aload_1
        19: iload_3
        20: i2d
        21: invokevirtual #4                  // Method record:(D)V
        24: aload_1
        25: invokevirtual #5                  // Method getAverage:()D
        28: dstore        4
        30: return
  • 6 ~ 9 行:新建了一个 ScoreCalculator 对象,使用 astore_1 存储在局部变量表 calculator 中,即把操作数栈栈顶的值(calculator)存储到局部变量表下标为 1 的位置上(dup 是啥,后面讲)
  • 10 ~ 13 行:iconst_1 和 iconst_2 用来将整数 1 和 2 加载到栈顶,istore_2 后 istore_3 用来将栈顶的元素存储到局部变量表下标为 2 和 3 的位置上
  • 14 ~ 17 行:对应 calculator.record(score1) 这行代码的执行,aload_1 从局部变量表下标为 1 的位置加载变量 calculator,iload_2 从局部变量表下标为 2 的位置加载变量 score1,i2d 将 int 类型的 score1 转为 double 类型的值后重新入栈,到此参数全部就绪,然后通过 invokevirtual 执行方法调用
  • 18 ~ 21 行:同上
  • 22 ~ 24 行:对应 double avg = calculator.getAverage() 这行代码的执行,aload_1 从局部变量表下标为 1 的位置加载变量 calculator,没有参数,到此参数全部就绪,然后通过 invokevirtual 执行方法调用,再使用 dstore 4 将存储方法返回值 avg 到局部变量表下标为 4 的位置

注意上面的第 5 行,为什么 locals 为 6 呢?

stack=3, locals=6, args_size=1

局部变量表
avg 是 double 类型变量,需要两个槽位 slot

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值