java 泛型 字节码原理_深入理解Java虚拟机:字节码执行引擎

本文详细介绍了Java虚拟机的栈帧结构,包括局部变量表、操作数栈、动态链接和方法返回地址。阐述了方法调用的过程,如静态调用、动态分派等,并对比了基于栈的指令集与基于寄存器的指令集的差异。此外,还解释了解释器如何执行字节码指令。
摘要由CSDN通过智能技术生成

一、运行时栈帧结构

2ef9522fdb2dcffea9d2dafc55338914.png

栈帧结构

  1. 栈帧:用于支持虚拟机进行方法调用和方法执行的数据结构,也是虚拟机运行时数据区中的虚拟机栈的栈元素。
  2. 栈帧存储了方法的局部变量表、操作数栈、动态链接、方法返回地址。
  3. 在活动线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是有效的。
1)局部变量表
  1. 一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。
  2. 变量槽(Slot)为最小单位
  3. boolean、byte、char、short、int、float、reference、returnAddress分别占用一个变量槽。
  4. long、double分别占用两个变量槽。
  5. 当一个方法被调用时,Java虚拟机会使用局部变量表来完成参数值到参数变量列表的传递过程,即实参到形参的传递。
  6. 当执行的是非static修饰的方法时,布局变量表中第0位索引的变量槽默认为传递方法所属对象实例的应用。
  7. 为了节省栈帧耗用的内存空间,局部变量表中的变量槽是可以重用的。
2)操作数栈
  1. 一个后进先出的栈。
  2. 当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法执行过程中,会有各种字节码指令向操作数栈中写入和读取内容,即出栈入栈操作。
  3. 优化处理:令两个栈帧出现一部分重叠;节约空间、在记性方法调用时可以直接公用一部分数据,无须进行额外的参数赋值传递了。
1ca5c8130f192b6fe43666bbd9e5bca3.png

实际栈帧重叠结构

3)动态链接
  1. 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接
4)方法返回地址
  1. 当一个方法开始执行后,只有两种方式能退出该方法。
  2. 正常调用完成:执行引擎遇到任意一个方法返回的字节码指令。
  3. 异常调用完成:在方法执行的过程中遇到了异常,并且没有catch或throw的异常。

二、方法调用

1)解析
  1. 调用不同类型方法的指令
    1. invokestatic:调用静态方法。
    2. invokespecial:调用实例构造器()方法、私有方法和父类的方法。
    3. invokevirtual:调用所有的虚方法。
    4. invokeinterface:调用接口方法。
    5. invokedynamic:运行时动态解析出调用点限定符引用的方法,在执行该方法。
  1. 方法调用方式:静态(在编译期间就完全确定,在类加载的解析阶段就会把符号引用转变为直接引用);分派
2)分派
  1. 静态分派
    1. 所有依赖静态类型来决定方法执行版本的分派动作。
    2. 典型应用表现:方法重载
  1. 动态分派
    1. 典型应用表现:方法重写
  1. 单分派与多分派
  2. 虚拟机动态分派的实现
  3. invokevirtual运行时解析过程
    1. 找到操作栈顶的第一个元素所指向的对象实际类型C。
    2. 如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限检验,如果通过则返回这个方法的直接引用,查找过程结束。不通过则返回java.lang.IllegalAccessError异常。
    3. 否则,按照继承关系从下往上依次堆C的各个父类进行第二步的搜索和验证过程。
    4. 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

三、基于栈的字节码解释执行引擎

1)解释执行
abdcb195746175e2b0a6fec16e0fdfc0.png

解释执行过程

2)基于栈的指令集与基于寄存器的指令集
  • 基于栈的指令集:Javac编译器输出的字节码指令流,字节码指令流里面的指令大部分都是零地址指令,它们依赖操作数栈进行工作。
  • 举例说明:

iconst_1

iconst_1

iadd

istore_0

  • 基于寄存器的指令集:主流PC机中物理硬件直接支持的指令集架构,依赖寄存器进行工作。
  • 举例说明:

mov eax, 1

add eax, 1

  • 特点:
    • 基于栈的指令集可移植,基于寄存器的指令集由硬件控制,受到硬件的约束;
    • 基于栈的指令集代码相对更加紧凑(字节码中每个字节就对应一条指令,而多地址指令集中还需要存放参数),但是完成相同功能所需的指令数量也会多。
    • 编译器实现更加简单(不需要考虑空间分配的问题,所需空间都在栈上操作)
    • 基于栈的指令集执行速度相对慢
    • 基于栈的指令集实现在内存中,频繁的内存访问,是执行速度的瓶颈
3)基于栈的解释器执行过程
b81e4b6b4346fc575c8649ff39161520.png
27e85bb6125bd1079dc863f5e51a9998.png
  1. 执行偏移地址为0的指令,bipush指令的作用是将单字节的整形常量值(-128~127)推入操作数栈顶,跟随一个参数,指明推送的常量值100。
6979baf1dbc407e54aaf130234655b08.png
  1. 执行偏移地址为2的指令,istore_1指令的作用是将操作数栈顶的整型值出栈并存放在第1个局部变量槽中。同理,偏移量地址为3,4,7,10的指令操作。
92b760b07b3092e99645cf8ed9ca01a3.png
  1. 执行偏移地址为11的指令,iload_1指令的作用是将局部变量表第1个变量槽中的整形值复制到操作数栈顶。
e07da1d61b0525ec350197a0f990270d.png
  1. 执行偏移地址为12的指令,iload_2指令的执行过程与iload_1类似,把第2个变量槽的整型值入栈。
5c967a8d8ed35b58e8120035e8db7eb7.png
  1. 执行偏移地址为13的指令,iadd指令的作用是将操作数栈中头两个栈顶元素出栈,做整形假发,然后把结果重新入栈。在iadd指令执行完毕后,栈中原有的100和200被出栈,它们的和300被重新入栈。
63bacbd8dadc5537b2141b11ad470cb7.png
  1. 执行偏移地址为14的指令,iload_3指令把存放在第3个局部变量槽中的300入栈到操作数栈中。这时操作数栈为两个整数300,。下一条指令imul是将操作数栈中头两个栈顶元素出栈,做整形乘法,然后把结果重新入栈,与iadd一样。
0b24c822b99150b29ba306edd42d226d.png
  1. 执行偏移地址为16的指令,ireturn指令是方法返回指令之一,它将结束方法执行并将操作数栈顶的整型值返回给该方法的调用者。
e2836403921490935abf604927b6cfa3.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值