JVM method invocation

JVM method invocation

 Method invocation is not equal to method execution. The goal of the method invocation phase is to determine which method is called. The target method in all method invocations is a symbolic reference in a constant pool in the Class file. In the class loading phase, it will be Converting some of the symbolic references into direct references, this kind of resolution can be established if the method has a determinable invocation version before the program runs, and the invocation version of this method is immutable during runtime. The invocation of such methods is called parsing.

Instructions to call the method:

invokestatic:Invoke a class (static) method

invokespecial : Invoke instance method; special handling for superclass, private, and instance initialization method invocations parent class method (that is, super.methodName() call method), private method and constructor

invokevirtual:Invoke instance method; dispatch based on class

invokeinterface:Invoke interface method

invokedynamic:Invoke dynamic method

 

As long as the methods that can be invoked by the invokestatic and invokespecial instructions can determine the unique invocation version in the parsing phase, including static methods, private methods, constructors, and parent class methods, these methods will resolve the symbolic reference when the class is loaded. for direct reference.

1. Static dispatch

 Look at the following code and its output:

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); //output animal
		ref.method(cat); //output animal
		ref.method(dog); //output animal
	}
}

 For Animal cat = new Cat(), Animal is called the static type (Static Type) of the cat variable, and Cat is called the actual type (Actual Type) of the cat variable. The static type of the variable itself will not change, but the actual type can be Changes, such as cat = new Dog(), can change the actual type of the cat variable to Dog, the static type is known at compile time, and the actual type is known at runtime.

 

Look at the bytecode of the main method:

 

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;

}

 You can see that there are three invokevirtual instructions in lines 35-46, which are actions to call three methods respectively. You can see that the target methods are the same: Method method:(Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V .

 

 

invokevirtual instruction

 

parameters  ...,  objectref , [ arg1 , [ arg2  ...]] →

 

Invoke instance method; dispatch based on class, call instance method

 

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, then m 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.

Therefore, in the process of calling the above code, objectref is ref, and jvm goes to ref to refer to the class of the object (that is, the StaticDispatch class) to find the method whose descriptor is (Lcc/lixiaohui/demo/dispatch/StaticDispatch$Animal;)V, very The obvious method method(Animal animal) meets the requirements, and the descriptor of method(Cat cat) is (Lcc/lixiaohui/demo/dispatch/StaticDispatch$Cat;)V, and the descriptor of method(Dog dog) is (Lcc/lixiaohui/ demo/dispatch/StaticDispatch$Dog;)V, none of them meet the requirements.

 

When overloading, the dispatch lookup is based on the static type rather than the actual type.

 

2. Dynamic allocation

Look at the code and its result:

 

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
	}
}

 The say() method is an instance method, so it is also called by the invokevirtual instruction. As long as the dispatching process of the invokevirtual instruction mentioned above is applied, you can know the implementation principle of Java's polymorphism. For example, when cat.say() is executed (the method descriptor For say:()V), the jvm goes to the class of the object referenced by cat (that is, the actual type Actual Type), that is, the same method for finding the descriptor in the Cat class (instead of Animal), obviously you can find Cat.say( ) method, so the method is called. The inheritance relationship here is relatively simple, but even if the inheritance relationship is very complicated, the routine is still this routine. Start looking for the actual type, if you can't find it, go to the parent class to find it, recursively find it... Finally, if you don't find it, throw an exception.

 

Override is by actual type instead of static type as the basis for dispatch lookup

 

 

refer to:

"In-depth understanding of virtual machines"

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokevirtual

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326142959&siteId=291194637