转载

JVM(8)-虚拟机字节码执行引擎

在活动线程中,只有栈顶的栈时有效的,称为 当前栈帧 ,与这个栈帧相关联的方法称为 当前方法 。下面对栈帧的4个主要部分进行分析。

局部变量表

存放方法参数和方法内部定义的局部变量

一些细节:

  • 最小存储单元(Slot),一个Slot可以存32位以内的数据类型。
  • boolean、byte、char、short、int、float、reference、returnAddress 占一个Slot; long、double 占两个Slot,是非原子的,但它是 线程安全 的,因为它是栈中的,是线程私有的。
  • 对于实例方法,第一个Slot是传递所属对象实例的引用,也就是我们常用的 this
  • 注意局部变量 没有初始值 哦。
  • 当一个变量的pc寄存器的值大于Slot的作用域时,Slot是可以 复用 的。

操作数栈

  • 虚拟机字节码执行引擎是“ 基于栈的执行引擎 ”,这个栈就是操作数栈。
  • 对比 基于寄存器 的执行引擎

优点:可移植性、代码更加紧凑、编译器实现更紧凑。确定就是速度更慢。

  • 下面栈帧的部分操作数栈与上面的栈帧的部分局部变量是重叠的。

动态连接

指向运行时常量池中该栈帧所属方法的引用,这个引用的为了支持方法调用过程的动态连接。具体内容在下面的方法调用中解释。

方法返回地址

方法退出(也就是 当前栈帧出栈 )的两种方式:

return

方法调用

方法调用不等同于方法的执行,方法调用阶段唯一的任务就是确定被调用方法的版本。说白了就是找方法,方法唯一就直接确定( 解析 )。方法不唯一: 重载(静态分配)、重写(动态分配)

解析调用

  • 调用目标在程序代码写好、编译器进行编译时就确定好的。这类方法时调用称为解析。是静态的。

  • 非虚方法:静态方法、私有方法、实例构造器、父类方法、final方法。它们都是采用解析调用。反之其它就是虚方法。

分派调用

Human man = new Man();

Human 是静态类型(外观类型), Man 是实际类型

静态分派

依赖静态类型来定位方法执行的版本的分配动作称为静态分配。最典型的应用是方法重载。

  • 编译器在 重载 时是根据参数的 静态类型 作为依据的。
  • 由于字面量没有显式的静态类型,它重载时可能会有多种选择,只是选一个更好的版本。
  • 静态分配和解析不是互斥的,例如静态方法也是可以重载的。
  • 注意:静态分配更严格, 一定是要用静态类型做参数

动态分派

依赖实际类型来定位方法执行的版本的分配动作称为动态分配。最典型的应用是方法重写。

  • 虚拟器在 重写 时是对象的 实际类型 作为依据的。
  • 注意:动态分配更宽松,如果实际类型中没有对应的方法,就会向上找父类里的相同方法来调用。

一个测试例子:

public class MixTest {
    static class Human{ }
    static class Man extends Human{}
    static class Woman extends Human{}

    public static class Father {
        public void choice(Human arg) {
            System.out.println("father choose human");
        }

        public void choice(Man arg) {
            System.out.println("father choose man");
        }

        public void choice(Woman arg) {
            System.out.println("father choose woman"); // 和同一类里的同名方法是重载关系
        }
    }

    public static class Son extends Father {
        public void choice(Human arg) {
            System.out.println("son choose human");
        }

        public void choice(Man arg) {
            System.out.println("son choose man"); // 和父类的同名同参数方法是重写关系
        }

        public void choice(Woman arg) {
            System.out.println("son choose woman");
        }
    }

    public static void main(String[] args) {
        Human woman = new Woman();
        Man man = new Man();
        Father father = new Father();
        Father son = new Son();
        father.choice(woman); // 重写:对象类型选Father(实际类型) 重载:参数类型选 Human(静态类型)
        son.choice(man); // 重写:对象类型选Son(实际类型) 重载:参数类型选 Man(静态类型)
    }
}

/* 程序输出:
father choose human
son choose man
 */
复制代码
原文  https://juejin.im/post/5c9d86d4e51d453b255eba14
正文到此结束
Loading...