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虚拟机--虚拟机字节码执行引擎的更多相关文章

  1. 深入理解Java虚拟机(字节码执行引擎)

    深入理解Java虚拟机(字节码执行引擎) 本文首发于微信公众号:BaronTalk 执行引擎是 Java 虚拟机最核心的组成部分之一.「虚拟机」是相对于「物理机」的概念,这两种机器都有代码执行的能力, ...

  2. 深入理解java虚拟机(5)---字节码执行引擎

    字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...

  3. 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性

    我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...

  4. 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的

    概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...

  5. 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎

    五 虚拟机字节码执行引擎   1 运行时栈帧结构     ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素.     ---栈帧中存储了方法的局部变 ...

  6. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  7. java虚拟机字节码执行引擎

    定义 java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.在不同的虚拟机实现里,执行引擎在执行java代码 ...

  8. Java虚拟机-字节码执行引擎

    概述 Java虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,成为各种虚拟机执行引擎的统一外观(Facade).不同的虚拟机引擎会包含两种执行模式,解释执行和编译执行. 运行时帧栈结构 栈帧(Sta ...

  9. JAVA虚拟机:虚拟机字节码执行引擎

    “虚拟机”是一个相对“物理机”的概念,这两种机器都有代码执行能力. 物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的. 虚拟机的执行引擎由自己实现,自行制定指令集与执行引擎的结构体系 ...

随机推荐

  1. [机翻] WIRER ON THE WIRE - SIGNALR协议的非正式描述

    原文 原文很简单,以下为机翻 WIRER ON THE WIRE - SIGNALR协议的非正式描述 我已经看到询问有关SignalR协议的描述的问题出现了很多.哎呀,当我开始关注SignalR时,我 ...

  2. 提取PPT中的原始图片

    写Word的不如做Excel 的,做Excel不如做PPT的,写代码的不如做PPT. 为了在电子邮件中上传PPT,其中的图片大都经过压缩,以便缩小PPT的体积.那么如何将PPT中的图片,还原成没有经过 ...

  3. Vue2.5开发去哪儿网App 搜索功能完成

    效果展示: Search.vue: <div class="search-content" ref="search" v-show="keywo ...

  4. vue中请求本地的json数据

    为什么要请求本地的数据?模拟后台的请求数据,验证页面的逻辑是否存在问题,抛开后台提前开发等. 常用的说来有:jq的方式 约等于 axios的方式,vuex状态管理的方式 个人认为最好用的就是jq的方式 ...

  5. 一、Linq简介

    语言集成查询Language Integrated Query(LINQ)是一系列将查询功能集成到C#语言的技术统称. 传统数据查询的缺点: 简单的字符串查询,没有编译时类型检查或Intellisen ...

  6. Python爬虫实战四之抓取淘宝MM照片

    原文:Python爬虫实战四之抓取淘宝MM照片其实还有好多,大家可以看 Python爬虫学习系列教程 福利啊福利,本次为大家带来的项目是抓取淘宝MM照片并保存起来,大家有没有很激动呢? 本篇目标 1. ...

  7. Anaconda 科学计算环境与包的管理

    相信大多数 python 的初学者们都曾为开发环境问题折腾了很久,包管理和 python 不同版本的问题,特别是 window 环境安装个 scrapy 各种报错 ,使用 Anaconda 可以很好的 ...

  8. 查漏补缺之开g的正则

    当正则表达式开了挂,就会多一个g的修饰符,用于表示全局匹配.然而这个表达式却不仅仅是多了个g这么简单,它的方法也会发生改变.由于之前不是太了解,今天好好捋一下,且听我细细道来. 正则表达式的方法和属性 ...

  9. Redis开发与运维

    常用命令 redis-server启动redis redis-server /opt/redis/redis.conf    配置启动 redis-server --port 6379 --dir / ...

  10. 【详解】JNI (Java Native Interface) (二)

    案例二:传递参数给C代码,并从其获取结果 注:这里传递的参数是基本类型的参数,在C代码中有直接的映射类型. 此案例所有生成的所有文件如下: (1)编写案例二的Java代码,如下: 这里我们定义了一个n ...