JVM-虚拟机栈之动态链接

  • Post author:
  • Post category:其他


*****标题写的是介绍虚拟机栈中的动态链接,但是这里讲解的是

动态链接



静态链接

的区别*****

一.概述:

Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以指向常量池的引用作为参数

  • 部分符号引用在类加载阶段(解析)的时候就转化为直接引用,这种转化为

    静态链接
  • 部分符号引用在运行期间转化为直接引用,这种转化为

    动态链接

二.虚/非虚方法

1.非虚方法:

在类加载阶段(解析时期)才会将符号引用解析直接引用的方法被称之为

非虚方法

  • 静态方法、私有方法、实例构造器、父类方法,这些只需要在类加载阶段就会确定
  • 解析调用一定是一个静态过程
  • 编译器可知,运行期不可变

2.虚方法:

在运行期间将符号引用转化为直接引用的方法被称之为虚方法

三.分派

首先先了解两个概念静态类型和实际类型

public class Ocean {
 
    public static void main(String[] args) {
        Ocean river = new River();
    }
}

class River extends Ocean{
}

Ocean river = new River();

静态类型:编译期间确定的类型(Ocean)

实际类型:运行期间确定的类型(River)

**********左为静态类型,右为实际类型

再了解一个概念宗量:方法的接受者(亦即方法的调用者)与方法的参数统称为方法的宗量

1.静态分派

class Ocean {
}

class River extends Ocean{
}

class Lake extends Ocean{
}

/**
 * 静态分派
 */
public class StaticDispatch{

    public void getSize(Ocean waterArea){
        System.out.println("Ocean is the biggest!");
    }
    public void getSize(Lake waterArea){
        System.out.println("Lake is bigger!");
    }
    public void getSize(River waterArea){
        System.out.println("River is big...");
    }

    public static void main(String[] args) {
        StaticDispatch dispatch = new StaticDispatch();
        Ocean river = new River();
        Ocean lake = new Lake();
        dispatch.getSize(lake);
        dispatch.getSize(river);
    }
}

控制台输出:

Ocean is the biggest!
Ocean is the biggest!

分析:

①定义了两个静态类型相同而实际类型却不同的变量

②重载时方法的执行依赖的是形参列表,而形参列表指的却是静态类型

③静态类型是在类加载(解析时期)就确定下来的

总结:静态分派可以解释重载

2.动态分派

abstract class Human {
    abstract void call();
}

class Father extends Human{
    @Override
    void call() {
        System.out.println("I am the Father!");
    }
}

class Mother extends Human{
    @Override
    void call() {
        System.out.println("I am the Mother!");
    }
}

public class DynamicDispatch {
    public static void main(String[] args) {
        Human father = new Father();
        Human mother = new Mother();
        father.call();
        mother.call();
    }
}

控制台输出:

I am the Father!
I am the Mother!

程序执行过程的机器指令分析如下:

各操作指令解析:

  • 0:在java堆中为变量father分配空间,并将地址压入操作数栈顶
  • 3:复制操作数栈顶值.并压入栈顶(此时操作栈上有两个连续相同的father对象地址)
  • 4:从操作栈顶弹出一个this的引用(即两个连续father对象地址中靠近栈顶的一个),并调用实例化方法<init>:()v
  • 7:将栈顶的仅剩的一个father对象地址存入第二个本地变量表slot(1)中
  • 8~15:重复上面的操作,创建了mother对象并将其地址存入第三个本地变量表slot(2)中
  • 16.将第二个本地变量表slot(1)中引用类型数据father地址推送至操作栈顶
  • 17:调用虚方法,根据ather对象地址查询其call()方法并执行
  • 20~21:重复上面的操作,根据mother对象地址查询其call()并执行
  • 24:结束方法

总结:从上面的invokevirtual可以知道方法call()的符号引用转换是在运行时期完成的,所以可以说动态分派解释了重载

3.单分派&多分派

单分派是指根据一个宗量就可以知道调用目标()即应该调用哪个方法)

多分派需要根据多个宗量才能确定调用目标

总结:

java的静态分派属于多分派类型

java的动态分派属于单分派类型



Java 语言是一门静态多分派(方法重载)、动态单分派(方法重写)的语言.



版权声明:本文为FloatDreamed原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。