简介
作者在前段时间深入学习了Java虚拟机,并尝试自己实现一个虚拟机demo,今天来对近期学习进行一个总结,谈谈虚拟机中方法的调用到底是怎么样的。
JVM架构
JVM本质上是一个栈虚拟机,即所有操作都是基于出栈入栈的。在我们的jvm运行时区里面,分为了几大部分:
- 公共区域:
- 堆:用于存储对象实例,数组
- 方法区:存储静态方法,常量(final),类信息
- 线程私有:
- 虚拟机栈
- 本地方法栈
- 程序计数器
在HotSpot中讲虚拟机栈和本地方法栈其实合二为一了,这里以HotSopt为例进行说明。JVM在运行时会将部分数据读取到运行时区,进行处理。在虚拟机栈中,该栈的每层都是一个栈帧,一个栈帧实际上就是代表了一个被读取到的运行时方法。每个栈帧中又存在一个操作数栈,一个局部变量表,我们通过读取栈帧中的字节码指令来控制局部变量表和操作数栈之间的数据交互。局部变量表存储的时方法中定义的局部变量和方法参数
相关字节码指令
在JVM中,关于方法我们有5条字节码指令:
-
invokevirtual:用于调用对象的实例方法(非 private,且非构造函数)。这是最常见的方法调用指令,用于调用那些在运行时动态绑定的方法。
-
invokeinterface:用于调用接口方法。由于接口方法在运行时需要动态查找实现,所以需要一个专门的指令来处理。
-
invokespecial:用于调用构造函数、私有方法以及父类方法。这些方法都是在编译时就可以确定的,不需要在运行时动态查找。
-
invokestatic:用于调用静态方法。静态方法也是在编译时就可以确定的,不需要在运行时动态查找。
-
invokedynamic:用于调用动态方法。这是在 Java 7 中引入的一个新指令,主要用于支持动态语言的运行。这个指令在运行时会动态地生成或查找调用点,并且这个调用点在运行时可以改变。
方法使用
在我们的解释器中会定义字节码匹配,如果匹配到该字节码时方法调用相关指令,就会调用对应方法,然后会为该方法创建一个新的栈帧,并将这个栈帧推送到我们的虚拟机栈栈顶,然后执行方法内部操作,如果该方法调用另一个方法,JVM会再新建一个栈帧,用于存储另一个方法信息,当方法执行完,根据其方法描述符中的返回类型进行弹出栈帧,压入返回参数。如我们的返回为V,即void,那么为无返回值,直接弹出当前帧即可,如果为 Z,即返回值是一个整数,就会弹出当前操作数栈顶的值并存储在一个变量retVal中,然后将其压入我们调用者方法的操作数栈栈顶即实现了方法间的调用。