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

原文地址:https://www.cnblogs.com/sun-haiyu/p/9204291.html

时间: 2024-10-28 20:06:47

Java虚拟机--虚拟机字节码执行引擎的相关文章

深入JAVA虚拟机之字节码执行引擎

前言:class文件结构.类加载机制.类加载器.运行时数据区这四个java技术体系中非常重要的知识,学习完了这些以后,我们知道一个类是通过类加载器加载到虚拟机,存储到运行时数据区,而且我们也知道了我们方法体内的代码被编译成字节码保存在方法表中的code属性中,那么虚拟机又是怎么执行这些代码的,得出方法输出结果的呢?这一节我们就要来学习,关于虚拟机字节码执行引擎的相关知识.通过这章节的学习,我们要掌握一下知识点: 1.运行时栈帧结构 2.方法调用 3.基于栈的字节码执行引擎 运行时栈帧结构 栈帧是

JAVA 虚拟机类加载机制和字节码执行引擎

引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 java有一个口号叫做一次编写,到处运行.实现这个口号的就是可以运行在不同平台上的虚拟机和与平台无关的字节码.这里要注意的是,虚拟机也是中立的,只要是符合规范的字节码,都可以被虚拟机接受,例如Groovy,JRuby等语言,都会生成符合规范的字节码,然后被虚拟机所运行,虚拟机不关心字节码由哪种语言生成. 类文件结构 class类文件是一组

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

周志明的<深入理解Java虚拟机>很好很强大,阅读起来颇有点费劲,尤其是当你跟随作者的思路一直探究下去,开始会让你弄不清方向,难免有些你说的啥子的感觉.但知识不得不学,于是天天看,反复看,就慢慢的理解了.我其实不想说这种硬磨的方法有多好,我甚至不推荐,我建议大家阅读这本书时,由浅入深,有舍有得,先从宏观去理解去阅读,再慢慢深入,有条不紊的看下去.具体来说,当你看书的某一部分时,先看这部分的章节名,了解这部分这一章在讲什么,然后再看某一章,我拿"类文件结构"这一章来说,我必须

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

概述 Java虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,成为各种虚拟机执行引擎的统一外观(Facade).不同的虚拟机引擎会包含两种执行模式,解释执行和编译执行. 运行时帧栈结构 栈帧(Stack Frame)支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量.操作数栈.动态连接和方法返回地址等信息.方法调用开始到执行完成,对应这一个帧栈在虚拟机栈里面入栈和出栈的过程. 一个线程中

虚拟机字节码执行引擎

在前面的几篇文章里,从Java虚拟机内存结构开始,经历了虚拟机垃圾收集机制.Class类文件结构到后来的虚拟机类加载机制,一步步的进入到了Java虚拟机即Java底层的世界.在有了前面的基础之后,接下来就应该进入Java虚拟机最重要的部分了--虚拟机字节码执行引擎,毕竟,这是Java程序得以在不同机器上运行的核心部分. Java是通过实现Java虚拟机来达到平台无关的."虚拟机"的概念是相对于"物理机"来说的,两种机器都有执行代码的能力,不过物理机是直接面向处理器.

【011】【JVM——虚拟机字节码执行引擎】

 JVM--虚拟机字节码执行引擎 Java 虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,这个概念模型成为各种版本虚机执行引擎的统一外观(Facade).在不同的虚拟机实现里面,执行引擎在执行Java代码的时候可能会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择,也可能两者兼备,甚至还可能会包含几个不同级别的编译器执行引擎. 运行时栈帧结构 栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈

基于栈的虚拟机字节码执行引擎

一.虚拟机字节码执行引擎概述 虚拟机字节码执行引擎主要就是研究字节码指令具体怎样被执行.对于物理机器,指令的执行是直接建立在OS和硬件的基础上 对于字节码指令的执行就是直接建立在JVM上,然后通过JVM完成具体的字节码指令到机器指令的过程.一般来说虚拟机的执行的 字节码指令是基于栈的不是采用寄存器,主要考虑的原因跨平台. 虚拟机的执行引擎是有JVM规范定义的,可以自己定义指令集以及执行引擎来执行字节码指令.不同的JVM执行引擎的实现可能不同 总体来说一个线程对应的是一个虚拟机栈:线程代码中调用的

深入理解JVM虚拟机5:虚拟机字节码执行引擎

虚拟机字节码执行引擎 微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南

JVM总结(五):JVM字节码执行引擎

JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 基于栈的字节码解释执行引擎 基于栈的指令集与基于寄存器的指令集 JVM字节码执行引擎 虚拟机是相对于“物理机”而言的,这两种机器都有代码执行能力,其区别主要是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而虚拟机的执行引擎是自己实现的.因此程序员可以自行制定指令集和执行引擎的