java虚拟机面试干货(七)— class的执行

在之前的文章中,我们大致总结了JVM的执行过程如下:
这里写图片描述
当class文件被类加载器加载到内存(栈)后,由执行引擎对字节码进行解析或等效处理后,最后输出结果。下面就说说这个执行引擎是如何工作的。

栈帧

栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。
栈帧存储了方法的局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附件信息等信息。编译代码的时候,栈帧需要的局部变量表大小,操作数栈的深度等都已经存储在Code属性中。栈帧大小不受运行期变量数据的影响。每一个方法从调用开始至执行完成过程,都是对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。
对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame)。与这个栈帧相关联的方法称为当前方法(Current Method)。执行引擎的所有字节码指令都是只针对当前栈帧进行操作的,典型的栈帧结构如下:
这里写图片描述
下面说说栈帧几个重要组成部分:

局部变量表

存放方法参数和局部变量。以变量槽存放数据,变量槽有索引,从0开始。0是当前对象的引用,用this关键字可以获取。其余是其他参数和局部变量,虚拟机通过索引取值。局部变量不像类变量,不会赋初始值,没赋值过就不能使用。

操作数栈

方法进入时是空的,许多指令会不停的吧数据出入栈。算数指令会对栈内数据计算,写回结果;方法调用会用操作数栈传递参数。

动态链接

指向运行时常量池中,该栈桢所属方法的引用。为了支持方法调用过程中的动态链接。

方法返回地址

方法退出时,需要回到上层调用的位置。栈桢需要保存这个地址。

方法调用

方法调用并不等同于方法的执行,方法调用的唯一任务是:确定被调用方法的版本(即调用哪个方法)。由于Java编译成class文件时,只是保存了方法的符号引用,而没有直接引用(即方法的内存地址),故这里涉及到解析和分派两个步骤。

解析

所有方法调用的目标方法都是在class文件中一个常量池的符号引用,故在类加载的解析阶段会把一部分符号引用转化为直接引用。此解析成立的前提是:方法在程序真正运行之前就有一个可以确定的调用版本,且此方法的调用版本在运行期是不可改变的。符合此前提的方法的调用称为解析(Resolution),也就是说解析式一个静态过程。
在Java中,符合解析的方法主要包括静态方法和私有方法两类,前者是于类直接关联的;后者在外部不可访问。由于其特点决定其无法被重写或覆盖,故适合在类加载时加载解析。其实final修饰的方法,也是符合此约定的,会在类加载时解析。但由于其是使用invokevirtual调用的,故这里单独给出。
Java提供了五种调用方法的字节码指令:
invokestatic(调用静态方法);
invokespecial(调用实例构造器,私有方法和父类方法);
invokevirtual(调用所有的虚方法);
invokeinterface(调用接口方法,会再运行时确定一个接口的实现);
invokedynamic(先在运行时动态的解析出调用点限定符所引用的方法再执行该方法);
其中invokestatic和invokespecial指令调用的方法,都可以在解析阶段唯一确定调用版本,也就是会再类加载时会被解析。

分派

分派是实现Java多态性的基础,可能是静态的也可能是动态的,根据分派依据的宗量数又可分为单分派和多分派。

静态分派

依赖静态类型来定位方法执行版本的分派动作,称为静态分派。静态分派的最典型的应用就是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。
方法重载参数的匹配规则:char->int->long->float->double->装箱类->装箱类的接口类型->装箱类从下往上的父类型->可变参数类型。也就是说首先会进行自动转型,然后是装箱操作,接着是装箱后的接口(实现了多个接口则优先级一致),再然后父类(从下往上递归),最后才是可变参数。

动态分派

在运行期间根据实际类型来确定方法执行版本的分派调用过程称为动态分派。这和方法重写有着很密切的关联。涉及到invokevirtual指令,这个指令的多态查找规则如下:
1.找到操作数栈顶的第一个元素所指对象的实际类型 C ;
2.若类型C中找到与常量中的描述符和简单名称都匹配的方法,则进行访问权限校验,通过则返回此方法的直接引用,查找结束;未通过则返回java.lang.IllegalAccessError;
3.若没找到匹配的方法则按照继承关系从下往上依次对C的父类进行第2部的搜索和验证过程;
4.若最终没有找到合适的方法,则抛出java.lang.AbstractMethodError;

单分派和多分派

宗量即对方法的接收者与方法的参数的统称。根据一个宗量对目标方法进行选择为单分派;根据一个以上宗量对目标方法进行选择为多分派。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值