Java虚拟机--虚拟机字节码执行引擎
Java虚拟机--虚拟机字节码执行引擎
所有的Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。
运行时栈帧结构
用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机栈的栈元素。每一个方法从调用开始到执行完成的过程,都对应一个栈帧在虚拟机栈中的入栈出栈过程。
由于虚拟机栈是线程私有的,所以每一个线程都有一个自己的虚拟机栈,而每个虚拟机栈都是由许多栈帧组成。每一个栈帧都包括
- 局部变量表
- 操作数栈
- 动态连接
- 方法返回地址
- 额外附加信息
处于栈顶的称为当前栈帧,对于执行引擎,在活动线程中只有当前栈帧是有效的,与当前栈帧关联的方法称为当前方法。
局部变量表
用于存放方法参数和方法内定义的局部变量。虚拟机通过索引定位的方式使用局部变量表,局部变量表的容量以变量槽(Variable Slot)为最小单位。局部变量不像类变量那样有“准备阶段”,不会被赋予系统初始值,所以在定义局部变量时一定要对其赋值。
操作数栈
又被称为操作栈,当一个方法开始执行时,操作栈是空的,随着方法的执行,各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈的操作。如在进行算术运算时就是通过操作数栈来进行的。
动态连接
每个栈帧都包括一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时就转换为直接引用,这种转化称静态解析;另外一部分在每一次运行期间转化为直接引用,称为动态连接。
方法返回地址
方法开始执行后,只有两种方法可退出该方法:
- 正常完成出口:执行引擎遇到任意一个方法返回的字节码指令;
- 异常完成出口:执行过程中遇到异常,在本方法中没有搜索到匹配的异常处理器而导致的退出。
方法退出的过程实际上等同于将当前栈帧出栈,退出时可能执行的操作有:恢复上层方法的局部变量表和操作舒展,如有返回值,把返回值压入调用者栈帧的操作数栈中,调用PC计数值以指向方法调用指令后面的一条指令。
方法调用
方法调用不同于方法执行,方法调用只是确定要调用哪一个方法,还不涉及方法内部的具体运行过程。所有方法调用在Class文件中都是一个常量池的符号引用,在类加载甚至是运行期间才能确定目标方法的直接引用。在类加载的解析阶段,会将一部分的符号引用转化成直接引用(静态解析),这种解析能成立的前提:
- 方法在程序真正运行之前就有一个可确定的调用版本
- 且这个方法在运行期间不可改变
满足上述条件的方法主要有两大类
- 静态方法,直接与类型关联
- 私有方法,在外部不能被访问
这两种方法各自的特点决定了它们不能通过继承或者别的方式重写其他版本,因此它们适合在类加载阶段进行解析。
Java虚拟机提供了5条方法调用字节码指令
- invokestatic:调用静态方法
- invokespecial:调用实例构造器
<init>方法、私有方法和父类方法; - invokevirtual:调用所有的虚方法
- invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象;
- invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。
只要能被invokestatic、invokespecial指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器父类方法,它们可以在类加载时就把符号引用解析为该方法的直接引用。这些方法称为非虚方法,除开final方法外的其他方法都称为虚方法。
分派
分派调用可能是静态的也可能是动态的。
静态分派
静态分派:所有以来静态类型来定位方法执行版本的分派动作称为静态分配。静态分配和重载的关系密切。
什么是静态类型,举个例子,比如类Human、Man和Woman,其中Man和Woman继承了Human。
package exercise;
public class StaticDispatch {
static class Human {}
static class Man extends Human{}
static class Woman extends Human{}
public void someMethod(Human human) {
System.out.println("Human");
}
public void someMethod(Man man) {
System.out.println("Man");
}
public void someMethod(Woman woman) {
System.out.println("Woman");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
StaticDispatch s = new StaticDispatch();
s.someMethod(man);
s.someMethod(woman);
}
}
上述main方法中,称Human为静态类型,或者外观类型,而Man或者Woman被称为实际类型。
静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型在编译期时可知的;实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么。
// 实际类型变化
Human man = new Man();
man = new Woman();
// 静态类型变化
s.someMethod((Man) man);
s.someMethod((Woman) man);
编译器在重载时是通过参数的静态的静态类型而不是实际类型作为判断依据的。因此上面的例子中会打印两个"Human"而不是一个打印"Man"一个打印"Woman"。
如果在main中改为
s.someMethod((Man)man);
s.someMethod((Woman) woman);
将会分别打印"Man"和"Woman"。
动态分派
动态分配:在运行期间根据实际类型确定方法的执行版本的分配过程。动态分配和多态中的重写(Override)有密切的关联。
举个例子
package exercise;
public class DynamicDispatch {
static abstract class Human {
protected abstract void someMethod();
}
static class Man extends Human {
@Override
protected void someMethod() {
System.out.println("Man");
}
}
static class Woman extends Human {
@Override
protected void someMethod() {
System.out.println("Woman");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.someMethod();
woman.someMethod();
man = new Woman();
man.someMethod();
}
}
上面的例子会打印
Man
Woman
Woman
单分派和多分派
方法的接收者和方法参数统称为方法的宗量,根据分派基于多少种宗量,可以将分派分配划分为多分派和单分派。
如果在分派过程中既要依据方法接收者而要依据方法参数,就是多分派,Java的静态分派属于多分派;如果在分派过程中只有某一种宗量作为选择依据,其他宗量不会影响对虚拟机的选择,比如方法参数不影响虚拟机选择,唯一可以影响虚拟机选择的因素只有此方法的接收者,则是单分派,Java的动态分派属于单分派。
总结一下:目前Java是一门静态多分派、动态单分派的语言。
by @sunhaiyu
2018.6.16
Java虚拟机--虚拟机字节码执行引擎的更多相关文章
- 深入理解Java虚拟机(字节码执行引擎)
深入理解Java虚拟机(字节码执行引擎) 本文首发于微信公众号:BaronTalk 执行引擎是 Java 虚拟机最核心的组成部分之一.「虚拟机」是相对于「物理机」的概念,这两种机器都有代码执行的能力, ...
- 深入理解java虚拟机(5)---字节码执行引擎
字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...
- 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性
我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...
- 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的
概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...
- 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎
五 虚拟机字节码执行引擎 1 运行时栈帧结构 ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素. ---栈帧中存储了方法的局部变 ...
- 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)
目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...
- java虚拟机字节码执行引擎
定义 java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.在不同的虚拟机实现里,执行引擎在执行java代码 ...
- Java虚拟机-字节码执行引擎
概述 Java虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,成为各种虚拟机执行引擎的统一外观(Facade).不同的虚拟机引擎会包含两种执行模式,解释执行和编译执行. 运行时帧栈结构 栈帧(Sta ...
- JAVA虚拟机:虚拟机字节码执行引擎
“虚拟机”是一个相对“物理机”的概念,这两种机器都有代码执行能力. 物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的. 虚拟机的执行引擎由自己实现,自行制定指令集与执行引擎的结构体系 ...
随机推荐
- WPF ListBox的进阶使用(二)
项目中经常使用需要根据搜索条件查询数据,然后用卡片来展示数据.用卡片展示数据时,界面的宽度发生变化,希望显示的卡片数量也跟随变化.WrapPanel虽然也可以实现这个功能,但是将多余的部分都留在行尾, ...
- nodejs学习(imooc课程笔记, 主讲人Scott)
课程地址: 进击Node.js基础(一) 进击Node.js基础(二) 1. nodejs创建服务器 var http = require('http'); //加载http模块 //请求进来时, 告 ...
- Spring Framework 4.3.22.RELEASE Reference文档目录
<Spring Framework Reference Documentation 4.3.22.RELEASE> https://docs.spring.io/spring/docs/4 ...
- 微服务 - Eureka注册中心
我们来解决微服务的第一问题,服务的管理. 服务中心对外提供服务,需要对外暴露自己的地址.而consumer(调用者)需要记录服务提供者的地址.将来地址出现变更,还需要及时更新.这在服务较少的时候并不觉 ...
- Code Chef MINPOLY(计算几何+dp)
题面 传送门 题解 我们枚举这个凸多边形\(y\)坐标最小的点\(p_i\),然后对于所有\(y\)坐标大于等于它的点极角排序 我们预处理出\(s_{j,k}\)表示三角形\(p_i,p_j,p_k\ ...
- [ZJOI2010]基站选址(线段树优化dp)
坑待填. \(Code\ Below:\) #include <bits/stdc++.h> #define lson (rt<<1) #define rson (rt< ...
- PANIC: Missing emulator engine program for 'x86' CPU.
获取可用的Android模拟器 1:emulator -list-avds 获取可用的模拟器的名称(只用名称) 2: android list -avd 获取可用的模拟器(信息详细) 获取iO ...
- spring cloud学习(五) 配置中心
Spring Cloud Config为服务端和客户端提供了分布式系统的外部化配置支持.配置服务中心采用Git的方式存储配置文件,因此我们很容易部署修改,有助于对环境配置进行版本管理. 一.配置中心 ...
- a 便签实现 下载
如果想通过纯前端技术实现文件下载,直接把a标签的href属性设置为文件路径即可,如下: <a href="https://cdn.shopify.com/s/files/1/1545/ ...
- Testing - 软件测试知识梳理 - 软件可靠性测试
软件可靠性的基本概念 错误,缺陷,故障和失效 错误:指的是软件在生命周期中各个阶段的状态和行为与人们的期待不一致的偏差,不单单是软件系统本身,中间产品的偏差也算是软件错误 缺陷:指的是软件中一切不好的 ...