JVM 方法的调用
方法的调用不等于方法执行,方法调用阶段的目标是确定被调用的是哪一个方法,所有方法调用中的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载阶段,会将其中一部分符号引用转化为直接引用,这种解析能成立的条件是:方法在程序运行前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。这类方法的调用称为解析。
调用方法的指令:
invokestatic:Invoke a class (static
) method
invokespecial:Invoke instance method; special handling for superclass, private, and instance initialization method invocations父类方法(即super.methodName()这种调用方式)、私有方法和构造方法
invokevirtual:Invoke instance method; dispatch based on class
invokeinterface:Invoke interface method
invokedynamic:Invoke dynamic method
只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本、包括静态方法、私有方法、构造方法和父类方法,这些方法在类加载的时候就会把符号引用解析为直接引用。
一、静态分派
看下面的代码及其输出结果:
public class StaticDispatch { static class Animal {} static class Cat extends Animal{} static class Dog extends Animal {} public void method(Animal animal) { System.out.println("animal"); } public void method(Cat cat) { System.out.println("cat"); } public void method(Dog dog) { System.out.println("dog"); } public static void main(String[] args) { StaticDispatch ref = new StaticDispatch(); Animal animal = new Animal(); Animal cat = new Cat(); Animal dog = new Dog(); ref.method(animal); //输出animal ref.method(cat); //输出animal ref.method(dog); //输出animal } }
对于Animal cat = new Cat()来说,Animal称为cat变量的静态类型(Static Type),Cat称为cat变量的实际类型(Actual Type),变量本身的静态类型不会改变,但实际类型可以改变,比如cat = new Dog() 可以把cat变量的实际类型变为Dog,静态类型编译期就已可知,而实际类型运行时才可知。
看main方法的字节码:
public static void main(java.lang.String[]); Code: Stack=2, Locals=5, Args_size=1 0: new #1; //class cc/lixiaohui/demo/dispatch/StaticDispatch 3: dup 4: invokespecial #41; //Method "<init>":()V 7: astore_1 8: new #42; //class cc/lixiaohui/demo/dispatch/StaticDispatch$Animal 11: dup 12: invokespecial #44; //Method cc/lixiaohui/demo/dispatch/StaticDispatch$Animal."<init>":()V 15: astore_2 16: new #45; //class cc/lixiaohui/demo/dispatch/StaticDispatch$Cat 19: dup 20: invokespecial #47; //Method cc/lixiaohui/demo/dispatch/StaticDispatch$Cat."<init>":()V 23: astore_3 24: new #48; //class cc/lixiaohui/demo/dispatch/StaticDispatch$Dog 27: dup 28: invokespecial #50; //Method cc/lixiaohui/demo/dispatch/StaticDispatch$Dog."<init>":()V 31: astore 4 33: aload_1 34: aload_2 35: invokevirtual #51; //Method method:(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V 38: aload_1 39: aload_3 40: invokevirtual #51; //Method method:(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V 43: aload_1 44: aload 4 46: invokevirtual #51; //Method method:(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V 49: return LineNumberTable: line 27: 0 line 29: 8 line 30: 16 line 31: 24 line 33: 33 line 34: 38 line 35: 43 line 36: 49 LocalVariableTable: Start Length Slot Name Signature 0 50 0 args [Ljava/lang/String; 8 42 1 ref Lcc/lixiaohui/demo/dispatch/StaticDispatch; 16 34 2 animal Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal; 24 26 3 cat Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal; 33 17 4 dog Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal; }
可以看到35-46行中有三个invokevirtual指令就是分别调用三个方法的动作,可以看到其目标方法都是一样的:Method method:(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V。
invokevirtual指令
参数 ..., objectref, [arg1, [arg2 ...]] →
Invoke instance method; dispatch based on class,调用实例方法
Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:
- If C contains a declaration for an instance method
m
that overrides (§5.4.5) the resolved method, thenm
is the method to be invoked, and the lookup procedure terminates. - Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the direct superclass of C; the method to be invoked is the result of the recursive invocation of this lookup procedure.
- Otherwise, an
AbstractMethodError
is raised.
因此,在上面代码调用过程中,objectref就是ref,jvm去ref引用对象的类(也就是StaticDispatch类)中找描述符为(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V的方法,很明显方法method(Animal animal)符合要求,而method(Cat cat)的描述符为(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Cat;)V,method(Dog dog)的描述符为(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Dog;)V,都不符合要求。
重载时是通过静态类型而不是实际类型作为分派查找的依据。
二、动态分派
看代码及其结果:
public class DynamicDispatch { static class Animal{ void say(){ System.out.println("animal"); } } static class Cat extends Animal { void say() { System.out.println("cat"); } } static class Dog extends Animal { void say() { System.out.println("dog"); } } public static void main(String[] args) { Animal cat = new Cat(); Animal dog = new Dog(); cat.say(); // cat dog.say(); // dog cat = new Dog(); cat.say(); // dog } }
say()方法为实例方法,因此也是invokevirtual指令调用,只要套用上面说的invokevirtual指令的分派过程,就可以知道Java的多态的实现原理,例如当执行cat.say()时(该方法描述符为say:()V),jvm去cat引用的对象的类(也就是实际类型Actual Type),即Cat类中(而不是Animal)中查找描述符相同的方法,很明显可以找到Cat.say()方法,因此该方法被调用。这里继承关系比较简单,不果即使继承关系很复杂,套路还是这个套路,从实际类型开始找,找不到就去父类找,递归地找.....最后没找到就抛异常
重写是通过实际类型而不是静态类型作为分派查找的依据
参考:
《深入理解虚拟机》
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual
相关推荐
JVM 方法调用之动态分派详解 JVM 方法调用之动态分派是 Java 虚拟机(JVM)中的一种机制,用于在运行时确定方法的调用目标。...通过虚方法表的机制,JVM 能够动态地调用方法,提高 Java 程序的灵活性和可扩展性。
"JVM 方法调用之静态分派详解" 静态分派是JVM 方法调用中的一种机制,根据分派依据的宗量数可分为单分派和多分派。静态分派的典型应用是方法重载,发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行...
文章目录Class 文件格式字节码Class类的本质Class文件格式类加载机制加载验证准备解析初始化类加载器双亲委派机制栈桢JVM方法调用详解方法解析静态分派动态分派参考 Class 文件格式 一般情况下Java代码执行流程如下...
这个函数是JVM调用Java方法的核心逻辑,它会验证调用参数,初始化调用栈,然后实际执行方法。 在调用之前,JVM会进行一系列的检查和准备工作。例如,它会检查当前线程是否是Java线程,调用的方法是否已知,是否处于...
3. **获取JNIEnv指针**:成功初始化JVM后,我们获得一个`JNIEnv*`指针,它是C++与Java之间进行方法调用的关键。`JNIEnv`提供了很多函数,如`FindClass`, `GetMethodID`, `CallVoidMethod`等,用于操作Java对象和调用...
Java虚拟机(JVM)在执行Java代码时,会根据不同的方法调用场景使用不同的字节码指令。这里主要讨论的是`invokestatic`、`invokespecial`、`invokevirtual`、`invokeinterface`和`invokedynamic`这五种指令。 1. `...
在Java虚拟机(JVM)中,程序的执行过程是一个复杂而精细的机制,尤其是对于方法调用这一核心操作。本文将深入探讨JVM如何执行Java方法,特别是从`main()`方法开始的程序执行流程。 首先,我们都知道Java程序的入口...
远程方法调用(Remote Method Invocation,简称RMI)是Java平台提供的一种机制,它允许一个程序在不同的Java虚拟机(JVM)之间调用另一个程序的方法。这种技术使得分布式计算成为可能,使得开发者可以构建分布式应用...
RMI远程方法调用是Java平台上的一个关键特性,它允许Java对象在不同的JVM之间进行通信,从而实现分布式计算。RMI的核心理念是让开发者能够像调用本地方法一样调用远程对象的方法,简化了分布式系统的设计和实现。 *...
《深入解析JVM字节码调用图生成器——基于jvm-callgraph开源项目》 在Java虚拟机(JVM)的世界里,理解和优化代码执行性能是至关重要的。为了达到这一目的,开发者需要深入理解程序的运行时行为,其中就包括了类与...
PB 代码通过n_pbnitest对象(该对象为C++编译的pbni对象导入到pb)调用Demo.class对象getHelloWorld方法,返回字符串“Hello world!” 功能 C++编译的pbni的dll文件为PBNI.dll,该文件启动java虚拟机(JNI_...
Java远程方法调用(Remote Method Invocation,简称RMI)是Java平台提供的一种分布式计算模型,允许一个Java虚拟机(JVM)上的对象通过网络调用另一个JVM上对象的方法,如同调用本地对象一样。这一特性使得Java应用...
Java虚拟机是如何执行方法调用的?(上) Java虚拟机在执行方法调用时,会经历三个阶段的选择过程来确定目标方法。第一阶段是在不考虑基本类型自动装拆箱和可变长参数的情况下选取重载方法;第二阶段是在允许自动装...
堆存储对象实例,方法区存放类信息,程序计数器记录当前线程执行的指令地址,虚拟机栈保存每个方法的局部变量、操作数栈等,本地方法栈为JNI调用的本地方法服务。 3. 指令集:JVM使用一套基于栈的指令集,这些指令...
JVM 的本地接口(JNI)允许 Java 程序直接调用本地方法(通常是 C 或 C++ 编写的函数)。这为 Java 应用提供了与底层操作系统和硬件交互的能力,同时也增强了 Java 程序的性能和灵活性。通过 JNI,开发人员可以在...
- JVM指令是字节码,每条指令对应一个特定的操作,如加载和存储变量、算术运算、控制流程、对象创建和方法调用等。 - 指令手册会列出所有这些指令,比如`iconst_5`表示将整数5压入操作数栈,`aload_0`用于将局部...
- **本地方法栈**:与Java方法不同,本地方法栈为JNI(Java Native Interface)调用的C/C++等本地方法服务。 了解JVM的启动过程和工作原理对于优化Java程序性能至关重要。通过调整JVM参数,我们可以控制堆大小、...
Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许在不同的Java虚拟机之间透明地调用对象的方法。在RMI架构中,客户端能够像调用本地对象一样调用远程服务器上的对象...
当调用对象的方法时,JVM需要确定对象的类型并找到正确的方法实现。这涉及到查找方法所在的类,并通过指针间接调用。 ##### 创建和访问数组 创建数组时,JVM需要在堆中分配内存,并记录数组的大小和类型。访问数组...