第八章 虚拟机字节码执行引擎

2017-04-05  本文已影响14人  骊骅

[目录]
概述

1 概述

2 运行时栈帧结构

2.1 局部变量表

2.2 操作数栈

2.3 动态连接

2.4 方法返回地址

两种返回的方法:
正常完成出口(Normal Method Invocation Completion)异常完成出口(Abrupt Method Invocation Completion)
无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息

3 方法调用

方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。

3.1 解析

Java虚拟机里面提供了5条方法调用字节码指令

方法名 解释 备注
invokestatic 调用静态方法
invokespecial 调用实例构造器<init>方法、私有方法和父类方法
invokevirtual 调用所有的虚方法
invokeinterface 调用接口方法,会在运行时再确定一个实现此接口的对象
invokedynamic todo todo

只要能被invokestaticinvokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,符合这个条件的有静态方法私有方法实例构造器父类方法4类,它们在类加载的时候就会把符号引用解析为该方法的直接引用


invokestaticinvokespecial指令调用的方法 称为非虚方法,与之相
反,其他方法称为虚方法(除去final方法,后文会提到)

3.2 分派

3.2.1 静态分派

示例代码

package study8;

/**
 * Created by haicheng.lhc on 05/04/2017.
 *
 * @author haicheng.lhc
 * @date 2017/04/05
 */
public class StaticDispatch {

    static abstract class Human {
    }

    static class Man extends Human {
    }

    static class Woman extends Human {
    }

    public void sayHello(Human guy) {
        System.out.println("hello,guy!");
    }

    public void sayHello(Man guy) {
        System.out.println("hello,gentleman!");
    }

    public void sayHello(Woman guy) {
        System.out.println("hello,lady!");
    }

    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        StaticDispatch sr = new StaticDispatch();
        sr.sayHello(man);
        sr.sayHello(woman);
    }
}

结果分析:

我们把上面代码中的“Human”称为变量的静态类型(Static Type),或者叫做的外观类型(Apparent Type),后面的“Man”则称为变量的实际类型(Actual Type),静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生变量本身的静态类型不会被改变,并且最终的静态类型是在编译期可知的;而实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么.虚拟机(准确地说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的

3.2.2.动态分派

示例代码

package study8;

/**
 * Created by haicheng.lhc on 05/04/2017.
 *
 * @author haicheng.lhc
 * @date 2017/04/05
 */
public class DynamicDispatch {

    static abstract class Human {
        protected abstract void sayHello();
    }

    static class Man extends Human {
        @Override
        protected void sayHello() {
            System.out.println("man say hello");
        }
    }

    static class Woman extends Human {
        @Override
        protected void sayHello() {
            System.out.println("woman say hello");
        }
    }

    public static void main(String[] args) {
        Human man = new Man();
        Human woman = new Woman();
        man.sayHello();
        woman.sayHello();
        man = new Woman();
        man.sayHello();
    }
}

解析
使用javap分析

Compiled from "DynamicDispatch.java"
public class study8.DynamicDispatch extends java.lang.Object
  SourceFile: "DynamicDispatch.java"
  InnerClass:
   #9= #4 of #7; //Woman=class study8/DynamicDispatch$Woman of class study8/DynamicDispatch
   #11= #2 of #7; //Man=class study8/DynamicDispatch$Man of class study8/DynamicDispatch
   abstract #13= #12 of #7; //Human=class study8/DynamicDispatch$Human of class study8/DynamicDispatch
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method   #8.#22; //  java/lang/Object."<init>":()V
const #2 = class    #23;    //  study8/DynamicDispatch$Man
const #3 = Method   #2.#22; //  study8/DynamicDispatch$Man."<init>":()V
const #4 = class    #24;    //  study8/DynamicDispatch$Woman
const #5 = Method   #4.#22; //  study8/DynamicDispatch$Woman."<init>":()V
const #6 = Method   #12.#25;    //  study8/DynamicDispatch$Human.sayHello:()V
const #7 = class    #26;    //  study8/DynamicDispatch
const #8 = class    #27;    //  java/lang/Object
const #9 = Asciz    Woman;
const #10 = Asciz   InnerClasses;
const #11 = Asciz   Man;
const #12 = class   #28;    //  study8/DynamicDispatch$Human
const #13 = Asciz   Human;
const #14 = Asciz   <init>;
const #15 = Asciz   ()V;
const #16 = Asciz   Code;
const #17 = Asciz   LineNumberTable;
const #18 = Asciz   main;
const #19 = Asciz   ([Ljava/lang/String;)V;
const #20 = Asciz   SourceFile;
const #21 = Asciz   DynamicDispatch.java;
const #22 = NameAndType #14:#15;//  "<init>":()V
const #23 = Asciz   study8/DynamicDispatch$Man;
const #24 = Asciz   study8/DynamicDispatch$Woman;
const #25 = NameAndType #29:#15;//  sayHello:()V
const #26 = Asciz   study8/DynamicDispatch;
const #27 = Asciz   java/lang/Object;
const #28 = Asciz   study8/DynamicDispatch$Human;
const #29 = Asciz   sayHello;

{
public study8.DynamicDispatch();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 9: 0
   line 22: 4


public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=3, Args_size=1
   0:   new #2; //class study8/DynamicDispatch$Man
   3:   dup
   4:   invokespecial   #3; //Method study8/DynamicDispatch$Man."<init>":()V
   7:   astore_1
   8:   new #4; //class study8/DynamicDispatch$Woman
   11:  dup
   12:  invokespecial   #5; //Method study8/DynamicDispatch$Woman."<init>":()V
   15:  astore_2
   16:  aload_1
   17:  invokevirtual   #6; //Method study8/DynamicDispatch$Human.sayHello:()V
   20:  aload_2
   21:  invokevirtual   #6; //Method study8/DynamicDispatch$Human.sayHello:()V
   24:  new #4; //class study8/DynamicDispatch$Woman
   27:  dup
   28:  invokespecial   #5; //Method study8/DynamicDispatch$Woman."<init>":()V
   31:  astore_1
   32:  aload_1
   33:  invokevirtual   #6; //Method study8/DynamicDispatch$Human.sayHello:()V
   36:  return
  LineNumberTable:
   line 30: 0
   line 31: 8
   line 32: 16
   line 33: 20
   line 34: 24
   line 35: 32
   line 36: 36


}

虽然17 21 语句完全一样,但是结果却不一样,原因是:从invokevirtual指令的多态查找过程开始说起,invokevirtual指令的运行时解析过程大致分为以下几个步骤:

1)找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C。
2)如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;如果不通过,则返回java.lang.IllegalAccessError异常。
3)否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。
4)如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

3.2.3单分派与多分派

3.2.4 虚拟机动态分派的实现

3.3 动态类型语言支持

4 基于栈的字节码解释执行引擎

上一篇下一篇

猜你喜欢

热点阅读