JVM之字节码执行引擎
方法调用:
方法调用不同于方法执行,方法调用阶段唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不执行方法内部的具体过程。方法调用有,解析调用,分派调用(有静态分派,动态分派)。
方法解析:
解析调用一定是一个静态的过程,在编译期就完全确定,可以在类加载的解析阶段就把涉及的符号引用转化为直接引用,不会延迟到运行期再去完成。
所有方法调用的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中一部分符号引用转化为直接引用(前提是,方法在程序真正运行前就可以确定调用的版本,并且这个方法的调用版本在运行期不会改变,也就是说,调用目标在程序代码写好,编译器编译时就能确定下来)。这类方法的调用称为解析。
编译期可知,运行期不变,这样的方法主要有静态方法和私有方法这两大类,前者与类型关联,后者在外部不可被访问,这两种方法各自的特点决定了它们都不能通过继承或别的方式重写其他版本,因此它们都适合在类加载阶段进行解析。
与之对应的是,在Java虚拟机里面提供了5条调用字节码指令,有:
invokestatic:调用静态方法
invokespecial:调用实例构造器<init>方法,私有方法和父类方法。
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时在确定一个实现此接口的对象。
只要能被invodestatic,invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本(而invokevirtual,invokeinterface指令调用的方法则不行,因为他们2调用的方法需要去找到子类对应的方法(如果有的话),实现该接口的类的方法,这些都是在运行期确定的)。能被invokestatic,invokespecial指令对应的方法有静态方法,私有方法,实例构造器方法,父类方法,它们会在类加载的时候(准确说是在解析阶段)就会把符号引用转化为直接引用,这些方法称为非虚方法,与之对应的称为虚方法(final方法除外,即使它是虚方法,但是因为它不能被重写,可以在类加载的解析阶段就把涉及的符号引用转化为直接引用,不会延迟到运行期再去完成)。
分派之静态分派:
分派调用可能是静态的,也可能是动态的,根据分派的宗量数可分为单分派和多分派。分派调用过程将会揭示多态性特征的一些基本的体现,如重载,重写是怎样实现的。、
变量的类型有静态类型和实际类型之分,所以在加载器无法确定变量的实际类型,也就无法确定变量所调用方法的版本。
静态类型(或者称外观类型,C++中称为编译器类型),实际类型(C++中称为运行期类型)。这两个概念是实现多态的基础。
虚拟机(准确说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的,静态类型是编译期可知的,因为在编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。另外,编译器虽然能确定出方法的重载版本,但在很多情况在这个版本有多个选择,往往只能确定一个更加合适的。如,基础类型变量,以字符变量为例重载时首选其实际的类型(接触类型没有静态类型这一说),然后是int,long,Character,Serializable,Object.
分派之动态分派:
invokevirtual指令(执行所有的虚方法)的运行时解析过程大致分为以下几个步骤:
1.找到操作数栈顶的第一个元素所指向的对象(调用当前方法的对象)的实际类型,记作C
2.如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;如果不通过抛出java.lang.IllegalAccessError异常;
3.否则,按照继承关系从下往上依次对C的父类进行第2步的搜索和校验过程;
4.如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。
这个过程就是Java语言中方法重写的本质,在这个过程中将常量池中的类方法的符号引用转化为直接引用(针对于这些虚方法)。我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
单分派与多分派:
方法的接收者与方法的参数统称为方法的宗量。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。
对照实例来分析:
public class Dispath
{
static class QQ{} static class _360{} public static class Father {
public void hardChoice(QQ arg){
System.out.println("father choose qq");
} public void hardChoice(_360 arg){
System.out.println("father choose 360");
}
} public static class Son extends Father {
public void hardChoice(QQ arg){
System.out.println("son choose qq");
} public void hardChoice(_360 arg){
System.out.println("son choose 360");
}
} public static void main(String[] args){
Father father = new Father();
Father son = new Son();
father.hardChoice(new _360());
son.hardChoice(new QQ());
}
}
对于hardChoice()方法
1.编译器选择方法版本的过程,也就是静态分派的过程。这时选择目标方法的依据有两点:一点是静态类型是Father还是Son,二是方法参数是QQ还是360。这次选择结果的产物是产生了两条invokevirtual指令,两个指令的参数分别为常量池中指向Father.hardChoice(360)及Father.hardChoice(QQ)方法的符号引用。如图:
因为是根据两个宗量进行选择,所以Java语言的静态分派属于多分派类型。
2.运行阶段的选择,也就是动态分派的过程。在执行“son.hardChoice(new QQ())”这句代码时,即invokevirtual指令时,由于编译期已经决定目标方法的签名必须是hardChoice(QQ),虚拟机此时不会关心传进来的参数类型了,因为这时的参数的静态类型,实际类型已经对方法的选择不会产生影响了(已经进行了重载,剩下的只有重写了)。唯一产生影响的是方法调用者的实际类型是Father还是Son。因为只有一个宗量作为选择依据,所以动态分派属于单分派类型。
基于栈的字节码解释执行引擎:
许多Java虚拟机的执行引擎在执行Java代码时都有解释执行(通过解释器执行),编译执行(通过即时编译器产生本地代码执行)两种选择。
Java编译器输出的指令流,基本上是基于栈的指令集架构,指令流中的指令大部分是零地址指令,他们依赖操作栈进行工作。与之对应的是基于寄存器的指令集,如x86二地址指令集(主流PC机使用)。
基于栈的指令集优点:可移植,代码想对紧凑(一字节对应一个指令),编译器实现更加简单(不用考虑空间分配问题,所需空间在栈上操作)。
缺点:速度慢(因为指令数量多,内存访问频繁,访问栈属于内存访问)。
基于栈的解释器执行过程跟处理器执行方式相似。
JVM之字节码执行引擎的更多相关文章
- Java之深入JVM(6) - 字节码执行引擎(转)
本文为转载,来自 前面我们不止一次的提到,Java是一种跨平台的语言,为什么可以跨平台,因为我们编译的结果是中间代码—字节码,而不是机器码,那字节码在整个Java平台扮演着什么样的角色的呢?JDK1. ...
- JVM(6) 字节码执行引擎
编译器(javac)将Java源文件(.java文件)编译成Java字节码(.class文件). 类加载器负责加载编译后的字节码,并加载到运行时数据区(Runtime Data Area) 通过类加载 ...
- 一夜搞懂 | JVM 字节码执行引擎
前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...
- JVM学习笔记:字节码执行引擎
JVM学习笔记:字节码执行引擎 移步大神贴:http://rednaxelafx.iteye.com/blog/492667
- JVM总结(五):JVM字节码执行引擎
JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...
- 深入理解JVM虚拟机5:虚拟机字节码执行引擎
虚拟机字节码执行引擎 转自https://juejin.im/post/5abc97ff518825556a727e66 所谓的「虚拟机字节码执行引擎」其实就是 JVM 根据 Class 文件中给 ...
- JVM基础结构与字节码执行引擎
JVM基础结构 JVM内部结构如下:栈.堆. 栈 JVM中的栈主要是指线程里面的栈,里面有方法栈.native方法栈.PC寄存器等等:每个方法栈是由栈帧组成的:每个栈帧是由局部变量表.操作数栈等组成. ...
- 深入理解java虚拟机(5)---字节码执行引擎
字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...
- 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性
我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...
随机推荐
- 如何在线测试Exchange的速度
最新碰到了客户需要比较国内版和国际版的Office365的速度问题,微软提供在线工具测试 这里以Exchange 测试为例子,请参考. PS Onenote贴过来只能至图片,各位看官只能将就了 这里有
- Paper Reading - Learning like a Child: Fast Novel Visual Concept Learning from Sentence Descriptions of Images ( ICCV 2015 )
Link of the Paper: https://arxiv.org/pdf/1504.06692.pdf Innovations: The authors propose the Novel V ...
- 线性代数之——A 的 LU 分解
1. A = LU 之前在消元的过程中,我们看到可以将矩阵 \(A\) 变成一个上三角矩阵 \(U\),\(U\) 的对角线上就是主元.下面我们将这个过程反过来,通一个下三角矩阵 \(L\) 我们可以 ...
- FFM
转载自http://blog.csdn.net/jediael_lu/ https://blog.csdn.net/jediael_lu/article/details/77772565 点击率预估算 ...
- Linux内核设计笔记8——下半部
# 下半部笔记 1. 软中断 软中断实现 软中断是在编译期间静态分配,其结构如下所示,结构中包含一个接受该结构体指针作为参数的action函数. struct softirq_action{ void ...
- 计算器软件实现系列(五)策略模式+asp.net
一 策略模式代码的编写 using System; using System.Collections.Generic; using System.Linq; using System.Web; /// ...
- unordered_map(hash_map)和map的比较
测试代码: #include <iostream> using namespace std; #include <string> #include <windows.h& ...
- 第一周—Fortran语言学习
使用教材:Fortran95程序设计[彭国伦] 第二章 编译器的使用 编译结果的好坏 1.翻译正确 2.执行文件的运行效率 3.翻译出来的执行码的长短 4.编译过程花费的时间 5.编译器提供Debug ...
- TCP 的有限状态机
TCP 有限状态机的图中每一个方框都是 TCP 可能具有的状态. 每个方框中的大写英文字符串是 TCP 标准所使用的 TCP 连接状态名. 状态之间的箭头表示可能发生的状态变迁. 箭头旁边的字,表明引 ...
- 团队作业7——第二次项目冲刺(Beta版本)
团队作业7——第二次项目冲刺-Beta版本项目计划 团队作业7——第二次项目冲刺(Beta版本)-第一篇 团队作业7——第二次项目冲刺(Beta版本)-第二篇 团队作业7——第二次项目冲刺(Beta版 ...