Home  >  Article  >  Java  >  Using Java method calls to resolve static and dynamic dispatch

Using Java method calls to resolve static and dynamic dispatch

WBOY
WBOYforward
2023-04-20 23:22:211293browse

Method calling

When the program is running, method calling is the most common and frequent operation

Method calling is not equal to method execution:

  • The only task in the method calling phase is to determine the method version being called, that is, which method is called

  • Does not involve the specific running process inside the method

The compilation process of the Class file does not include the connection step in traditional compilation

All method calls in the Class file are symbolic references stored in the Class file, and It is not the entry address of the method in the actual running memory layout, that is, the previous direct reference:

  • This makes Java have more powerful dynamic expansion capabilities

  • At the same time, it also makes the Java method calling process relatively complicated.

  • It is necessary to determine the direct reference of the target method during class loading or even during running time

Method analysis

The target method in all method calls is a reference to a constant pool in the Class file

In the loading and analysis phase of the class, it will Convert some of the symbol references into direct references:

The method has a determinable calling version before the program is actually executed, and the calling version of this method is unchangeable during runtime

In other words, the calling target is completed in the program code and must be determined when the compiler compiles. This is also called method analysis

Java method classification

In Java, it conforms to the "compile time" It can be seen that there are two major categories of methods that are "immutable during runtime":

  • Static methods: are directly related to the type

  • Private method: Not accessible externally

The characteristics of these two methods determine that neither method can be overridden through inheritance or other methods. version, so it is suitable for parsing during the class loading phase

Non-virtual method: During the class loading phase, the symbol reference will be parsed as a direct reference to the method

  • Static method

  • Private method

  • Instance constructor

  • Parent class method

Virtual method: During the class loading phase, the symbol reference will not be resolved as a direct reference to the method

Except for the above non-virtual methods, other All methods are virtual methods

static dispatch

public class StaticDispatch {
	static abstract class Human {
	}
	static class Man extends Human {
	}
	static class Woman extends Human {
	}
	public static void sayHello(Human guy) {
		System.out.println("Hello, Guy!");
	}
	public static void sayHello(Man guy) {
		System.out.println("Hello, Gentleman!");
	}
	public static void sayHello(woman guy) {
		System.out.println("Hello, Lady!");
	}
	public static void main(String[] args) {
		Human man = new Man();
		Human women = new Woman();
		sayHello(man);
		sayHello(woman);
	}
}

Human man = new Human();

Human is the static type of the variable

Man is the actual type of the variable

Both the static type and the actual type will change in the program:

Static type:

  • Static type changes only occur when using the

  • variable The static type itself will not be changed

  • The final static type is known in the compiler

Actual type:

  • The result of the actual type change is determined during runtime

  • The compiler does not know what the actual type of an object is during compilation

Human human = new Man();
sayHello(man);
sayHello((Man)man);		// 类型转换,静态类型变化,转型后的静态类型一定是Man
man = new woman();		// 实际类型变化,实际类型是不确定的
sayHello(man);
sayHello((Woman)man);	// 类型转换,静态类型变化

When overloading, the compiler uses the static type of the parameter instead of the actual type as the basis for judgment. The static type can be known during compilation:

Compilation phase, The Javac compiler will decide which overloaded version to use based on the static type of the parameter

Static dispatch:

  • All methods that rely on static types to locate methods Execution version of dispatch action

  • Typical application: Method overloading

Static dispatch occurs during the compilation phase , Therefore, the action of determining static dispatch is not performed by the virtual machine, but is completed by the compiler.

Since the literal does not display a static type, it can only be understood and inferred through the rules of the language.

public class LiteralTest {
	public static void sayHello(char arg) {
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] arg) {
		sayHello('a');
	}
}

The compiler annotates the overloaded methods from top to bottom to get different outputs

If the compiler cannot determine which type to customize the conversion to, it will prompt type ambiguity and reject Compile

public class LiteralTest {
	public static void sayHello(String arg) {	// 新增重载方法
		System.out.println("Hello, String!");
	}
	public static void sayHello(char arg) {	
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] args) {
		Random r = new Random();
		String s = "abc";
		int i = 0;
		sayHello(r.nextInt() % 2 != 0 ? s : 1 );	// 编译错误
		sayHello(r.nextInt() % 2 != 0 ? 'a' : false);	//编译错误
	}
}

Dynamic dispatch

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 women = new Woman();
		man.sayHello();
		woman.sayHello();
		man = new Woman();
		man.sayHello();
	}
}

This is not determined based on the static type

  • Static typeHumanTwo variablesman and woman perform different behaviors when calling the sayHello() method

  • VariablesmanDifferent methods were executed in two calls

The reason for this phenomenon: The actual types of the two variables are different

How the Java virtual machine dispatches the execution version of the method according to the actual type: Starting from the polymorphic search process of the invokevirtual instruction , the runtime parsing process of the invokevirtual instruction It is roughly divided into the following steps:

  • Find the actual type of the object pointed to by the first element on the top of the operand stack, recorded as C

  • If a method matching the descriptor and simple name in the constant is found in type C, then access permission verification is performed. If the verification is passed, a direct reference to this method is returned, and the search process ends; If the verification fails, java.lang.illegalAccessErrorException

  • If not found, each type C element will be checked from bottom to top according to the inheritance relationship. The parent class performs the second step of search and verification process

  • If no suitable method is found, it will throw java.lang.AbstractMethodErrorException

The essence of Java language method overriding:

invokevirtualThe first step in instruction execution is to determine the actual status of the receiver at runtime type, so the invokevirtual instruction in the two calls resolves the class method symbol reference in the constant pool to a different direct reference.

This is a dispatch process that determines the method execution version based on the actual type during runtime. It’s called dynamic dispatch

The implementation of virtual machine dynamic dispatch

The modes of virtual machine concept analysis are static dispatch and dynamic dispatch. It can be understood that the virtual machine will "will What to do"This question

Virtual machine"How to do it specifically" There will be differences in various virtual machine implementations:

  • Because dynamic dispatch is a very frequent action, and the method version selection process of dynamic dispatch requires searching for a suitable target method in the method metadata of the class at runtime

  • Therefore, in the actual implementation of virtual machines, for performance reasons, most implementations will not actually perform such frequent searches

  • The most commonly used "stability optimization" The way is to create a virtual method table (vtable) for the class in the method area, use virtual method table index instead of metadata search to improve performance

The virtual method table stores the actual entry address of each method:

  • If a method is not called in the subclass If rewritten, the address entry in the virtual method table of the subclass is consistent with the address entry of the same method in the parent class, and both point to the actual entry of the parent class

  • If there is a duplicate in the subclass After writing this method, the address in the subclass method table will be replaced with the entry address pointing to the actual method of the subclass

Methods with the same signature, in the parent class, subclass The virtual method table of the class has the same index number:

In this way, when the type is changed, only the method table to be searched needs to be changed, and the required index can be converted from different virtual method tables according to the index. Entry address

The method table is generally initialized in the connection phase of the class loading phase:

After preparing the initial value of the variable of the class, the virtual machine will Also initialized

The above is the detailed content of Using Java method calls to resolve static and dynamic dispatch. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete