`

《虚拟机字节码执行引擎》笔记:方法调用

 
阅读更多

周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。

方法调用

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

 

解析

         所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,虚拟机会将其中的一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好、编译器进行编译时就必须确定下来。这类方法的调用称为解析(Resolution)。

         Java中,符合“编译器可知,运行期间不可变”的方法主要有静态方法和私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法都不可能通过继承或别的方式重写出其他版本,因此它们都适合在类加载阶段进行解析。

         可以在解析阶段确定唯一的调用版本的有静态方法、私有方法、实例构造器和父类方法四类,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法可以称为非虚方法final方法也是非虚方法。

        

分派

         解析调用一定是个静态的过程,在编译期间就完全确定,在类装载的解析阶段就会把涉及的符号引用全部转变为可确定的直接引用,不会延迟到运行期再去完成。而分派Dispatch)调用则可能是静态的也可能是动态的。

 

         静态分派

/**
 * 方法静态分派演示
 * @author 周志明
 */
public class StaticDispatch {
	static abstract class Human{
		
	}
	static class Man extends Human{
		
	}
	static class Women 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(Women guy){
		System.out.println("hello,lady!");
	}
	public static void main(String[] args) {
		Human man=new Man();
		Human women=new Women();
		
		StaticDispatch sd=new StaticDispatch();
		sd.sayHello(man);
		sd.sayHello(women);
	}
}

 

Human man=new Man();

我们把上面代码中的“Human”称为变量的静态类型(Static Type)或者外观类型(Apparent Type),后面的“Man”则称为变量的实际类型(Actual Type)。在main()里面的两次sayHello()方法调用,在方法接收者已经确定是对象“sd”的前提下,使用哪个重载版本,就完全取决于传入参数的数量和数据类型。代码中特意地定义了两个静态类型相同、实际类型不同的变量,但虚拟机(准确地说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。静态类型是编译期可知的,所以在编译阶段,Javac编译器就根据参数的静态类型决定使用哪个重载版本,所以选择了sayHello(Human)作为调用目标,并把这个方法的符号引用写到main()方法里的两条invokevirtual指令的参数中。

所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派。静态分派最典型的应用就是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。(在上面的代码中,如果把sayHello(Human guy)方法删掉,编译器将不能编译该类)。

 

动态分派

动态分派和重写(Override)有着密切的联系。

/**
 * 方法动态分派演示
 * @author 周志明
 */
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 Women extends Human {
		@Override
		protected void sayHello(){
			System.out.println("Women say hello");
		}
	}
	public static void main(String[] args) {
		//以下两句代码首先建立man和women的内存空间,将这两个实例的引用存放在第1和第2个局部变量表Slot之中。
		Human man=new Man();
		Human women=new Women();
		
		/*
		调用该方法前,把man对象的引用压到栈顶,man对象是sayHello()方法的所有者,称为接收者(Receiver)
		invokevirtual指令调用方法,invokevirtual步骤是:
		1、找到操作数栈顶的第一个元素所指向的对象的实际类型(在这里是Man),记作C。
		2、如果在类型C中找到与常量中描述符和简单名称都相符的方法(在这里是sayHello()方法),则进行访问权限校验,如果通过则返回这个方法的直接引用,查找结束;不通过则返回异常。
		3、否则按照继承关系从下往上一次对C的各个父类进行第2步的过程。
		4、如果始终没找到合适的方法,则抛出java.lang.AbstractMethodError异常。
		*/
		man.sayHello();
		
		//调用该方法前,把women对象的引用压到栈顶,……(同上)
		women.sayHello();
		
		man=new Women();
		man.sayHello();
	}
}

 

 

         由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同的直接引用上,这个过程就是Java语言中方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程成为动态分派。

分享到:
评论

相关推荐

    学习笔记:深入浅出 Java 虚拟机.docx

    JVM 是一个虚拟计算机,能够执行字节码(.class文件),这些字节码是Java源代码经过编译后的中间表示。JVM 作为抽象层,隔离了底层操作系统和Java程序,使得Java具有高度的跨平台能力。 JVM 和操作系统的关系可以...

    深入java虚拟机笔记

    - **执行引擎**:执行引擎负责解释执行字节码指令,并进行优化。 - **本地方法接口**:本地方法接口允许Java程序调用非Java代码。 - **真实机器**:这里的“真实机器”指的是JVM在特定硬件平台上的具体实现。 ##...

    深入Java虚拟机读书笔记1

    4. 硬件级别的虚拟机:在某些处理器上,JVM可以直接集成到硬件中,以本地方法执行字节码,达到更高的执行效率。 Java方法和本地方法是两种不同的调用机制。Java方法是用Java语言编写,被编译成字节码,独立于平台。...

    HelloWorld的javap -verbose HelloWorld 字节码初探

    源码是程序员编写的应用程序的原始形式,而字节码是Java虚拟机(JVM)执行的二进制代码,它是源码经过编译后的产物。`javap`作为工具,是理解和调试Java字节码的重要助手。 在文件名称列表中,“jvm指令学习笔记....

    理解虚拟机--有笔记版

    Java虚拟机规范定义了JVM在执行Java程序时必须遵守的行为准则,它包括类文件格式、字节码指令集、执行引擎的行为等。 除了上述提到的基础知识点外,《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》这本...

    深入理解Java虚拟机笔记(带目录).docx

    Java 虚拟机(JVM)是 Java 语言的运行环境,它负责解释和执行 Java 字节码。下面是 Java 虚拟机相关的知识点: 虚拟机内存结构 Java 虚拟机的内存结构主要包括以下几个部分: * 方法区(Method Area):用于存储...

    Java虚拟机讲解笔记.docx

    - **功能**: 当前线程所执行的字节码指令地址。 - **特点**: 是一块较小的内存空间,不会出现OutOfMemoryError。 ##### 5. 本地方法栈 (Native Method Stack) - **功能**: 为虚拟机使用到的Native方法服务。 - **...

    Java虚拟机JVM类加载学习笔记

    Java虚拟机(JVM)是Java程序的核心组成部分,它负责执行字节码并管理程序运行时的内存。本文主要探讨JVM的类加载机制,包括类加载、连接、初始化等关键过程,以及类的主动使用和被动使用的情况。 首先,我们要理解...

    深入理解Java虚拟机学习笔记借鉴.pdf

    程序计数器是 JVM 的一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,每条线程都需要有一个独立的程序计数器,各条线程之间程序计数器互不影响,独立存储,是线程隔离的。 本地方法栈的作用...

    学习笔记之java虚拟机

    - 当执行的是Java方法时,程序计数器记录的是当前线程所执行的字节码指令地址。 - 对于native方法,程序计数器的值为空(Undefined),表示此时该线程执行的是非Java语言(如C/C++)编写的本地方法。 2. **Java...

    学习笔记之对象的创建(Java)

    Java虚拟机的执行引擎负责解释或编译字节码指令,使其能够在特定的硬件平台上执行。Java虚拟机支持约248种字节码指令,每条指令对应一项基本的计算任务,如整数加法或子程序跳转等。指令的执行流程通常遵循以下模式...

    环境变量配置及JVM虚拟机笔记

    - **执行引擎(Execution Engine)**:解释执行字节码指令。 - **本地接口(Native Interface)**:提供与本地操作系统的接口。 ##### JVM与平台无关性 - **跨平台性**:JVM的一个重要特点是其跨平台的能力。无论是在...

    深入java虚拟机读后笔记

    简单来说,Java源代码在编译成`.class`文件后,并不是直接被操作系统执行,而是通过JVM来解释执行这些`.class`文件中的字节码。因此,无论是在Windows、Linux还是Mac OS等不同平台上,只要安装了相应的JVM版本,就...

    Smali学习笔记

    ##### 3.2 Dalvik虚拟机字节码的类型、方法和字段的表示方法 Dalvik字节码中,类型、方法和字段的表示方式如下: ###### 3.2.1 类型 类型表示一般形式为`L<包名>/<类名>;`,例如`Ljava/lang/String;`。 ###### ...

    java虚拟机源码-openjdk7:openjdk7源码(深入理解java虚拟机,读书笔记)

    3. **字节码执行引擎**:解释器和即时编译器(JIT)负责执行字节码。解释器逐条执行字节码,而JIT会将频繁执行的代码编译成机器码以提高性能。 4. **垃圾收集器**:自动管理内存,通过标记-清除、复制、标记-整理、...

    java虚拟机源码学习-UnderstandingTheJVM:深入理解Java虚拟机(周志明)源码及学习笔记

    3. **字节码执行引擎**:JVM通过解释器和Just-In-Time (JIT)编译器执行字节码。解释器逐行解释执行,而JIT编译器则将热点代码编译成机器码,提升运行效率。 4. **垃圾收集机制**:JVM自动管理内存,通过垃圾收集器...

    笔记,1、虚拟机的前世今生,深入理解JVM内存区域1

    1. 程序计数器(PC Register):指令相关堆、方法:数据相关程序计数器较小的内存空间,当前线程执行的字节码的行号指示器;各线程之间独立存储,互不影响。 2. 虚拟机栈(VM Stack):每个线程私有的,线程在运行...

    JVM的学习笔记PDF版

    类装载器负责加载类文件,运行数据区存储程序运行时的数据,执行引擎负责执行字节码,本地方法接口用于调用非Java语言编写的本地方法,而本地库则包含这些本地方法。 2. **类加载机制**:Java的类加载过程包括加载...

    jvm中篇笔记.md

    - 示例代码演示了如何通过字节码观察父类调用子类重写方法的行为,特别关注对象创建时的构造过程和成员变量的初始化顺序。 ##### 3. 虚拟机的基石:Class 文件 - **字节码文件的组成** - 字节码是一种中间代码...

    java虚拟机笔记及代码.zip

    Java虚拟机(JVM)是Java程序运行的核心,它是一个抽象的计算机系统,负责执行Java字节码。在深入理解JVM之前,我们先要明白什么是字节码:Java源代码经过编译后生成的中间表示,即.class文件,里面包含的就是字节码...

Global site tag (gtag.js) - Google Analytics