JAVA语言之虚拟机字节码执行引擎
小标 2018-08-01 来源 : 阅读 981 评论 0

摘要:本文主要向大家介绍了JAVA语言之虚拟机字节码执行引擎,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言之虚拟机字节码执行引擎,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步。

JAVA语言之虚拟机字节码执行引擎

一、运行时栈帧结构

  一个线程中的调用链可能很长,很多方法同时处于执行状态。对执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。

1.局部变量表

1)存储内容
  局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2)如何存储
  局部变量表以变量槽(Variable Slot)为最小单位。那Slot到底多大呢?虚拟机规范并没有明确指明一个Slot所占用的空间大小,只是很有导向地说每个Slot都应能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据,这8种数据类型都可使用32位或更小的物理内存来存放。Slot长度可随着处理器、操作系统或虚拟机不同而发生变化。只要保证即时在64位虚拟机中使用了64位的物理内存空间去实现一个Slot,虚拟机仍要使用对齐和补白的手段让Slot在外观上看起来与32位虚拟机中的一致。
3)如何使用
  虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。如果访问的是32位数据类型的变量,索引n就代表了使用第n个Slot,如果是64位数据类型的变量,则说明会同时使用n和n+1两个Slot。对于相邻的共同存放一个64位数据的两个Slot,不允许采用任何方式单独访问其中的某一个。
  在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程的。如果执行的是实例方法(即非static方法),那局部变量中第0位索引的Slot默认是用于传递方法所属对象实例的引用。在方法中可通过关键字“this”来访问到这个隐含的参数。接下来按声明顺序分配其余参数,占用从1开始的局部变量Slot,最后分配方法体中定义的其他变量。

2.操作数栈

1)存储内容
  存储方法执行过程中用到的数据。
2)如何存储
  32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2.
3)如何使用
  当一个方法刚刚开始执行的时候,这个方法的操作数栈的空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,即出栈/入栈操作。在调用其他方法时也是通过操作数栈来进行参数传递的。
  iadd在运行时操作数栈中最接近栈顶的两个元素已经存入两个int型的数值,当执行这个指令时,会将两个int值出栈并相加,然后将相加的结果入栈。

3.动态连接

  每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。

4.方法返回地址

存储内容:
  返回地址存储的内容由方法的不同退出方式决定。无论哪种退出方式,在方法退出后,都需要返回到方法调用的位置,程序才能继续执行。当方法正常退出时,调用者的程序计数器的值可作为返回地址,栈帧中很可能会保存这个计数器;而方法异常退出时,返回地址是通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。

二、方法调用

  方法调用不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的执行过程。Class文件的编译过程不包含传统编译中的连接步骤,一切方法在Class文件中存储的都是符号引用,而不是方法在实际运行时内存布局的入口地址。如何确定Java调用的具体方法呢?

1.解析

  所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,虚拟机会将一部分符号引用转为直接引用。具体是哪部分呢?方法在运行前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。
  对应的字节码指令是:
invokestatic:调用静态方法
invokespecial:调用实例构造器<init>方法、私有方法和父类方法
被这俩指令调用的方法,都可在解析阶段中确定唯一的调用版本,在类加载时就会把符号引用解析为直接引用。这些方法称为非虚方法,与之相反,其他方法称为虚方法(出去final方法)。final方法也是一种非虚方法。虚方法指令:
invokevirtual:调用所有虚方法
invokeinterface:调用接口方法,会在运行时确定一个实现此接口的对象
invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后执行该方法。前面4个指令的分派逻辑是固化在Java虚拟机内部的,而此指令的分派逻辑是由用户设定的引导方法决定的。

2.分派

1)静态分派
  所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。

Human man = new Man();

“Human”称为变量的静态类型,“Man”称为变量的实际类型。
2)动态分派
  典型应用是:方法重写。

三、方法执行

1.基于栈的执行过程

其字节码指令为:


某些jdk编译的字节码指令会包含:

Code:

   Stack=2, Locals=4, Args_size=1  //意思是这段代码需要深度为2的操作数栈和4个Slot的局部变量空间

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved