美文网首页
Java各个方法调用浅析

Java各个方法调用浅析

作者: 欧文不哭 | 来源:发表于2017-05-01 21:06 被阅读0次

    JVM方法调用的指令有以下五种:

    invokestatic 调用类方法(静态绑定,速度快)

    invokevirtual 调用实例方法(动态绑定)

    invokespecial 调用构造方法,私有方法及super关键字方法(静态绑定,速度快)

    invokeinterface 调用接口方法(动态绑定)

    invokedynamic JDK7引入的支持动态语言的方法调用

    让我们通过字节码实例来分析一下(例子是使用Java8编译的)

    package com.zou.basic.class1;
    
    import java.util.Arrays;
    
    public class A implements IA {
    
        public A() {
    
        }
    
        public static int add(int a, int b) {
            return a + b;
        }
    
        public int minus(int a, int b) {
            return a - b;
        }
    
        public int plus(int a, int b) {
            return a * b;
        }
    
        private int power(int a) {
            int hash = super.hashCode();
            return a * a;
        }
    
        public static void main(String[] args) {
            // invokestatic
            A.add(10, 20);
    
            // invokespecial
            A a = new A();
            a.power(10);
            // invokevirtual
            a.minus(20, 10);
            // invokeinterface
            IA a1 = (IA) a;
            a1.plus(10, 20);
            // invokedynamic
            Arrays.asList(1, 2, 3, 4).stream().forEach(n -> {
                System.out.println(n);
            });
        }
    
    }
    
    interface IA {
    
        int plus(int a, int b);
    
    }
    

    相应的main方法的字节码如下

    // class version 52.0 (52)
    // access flags 0x21
    public class com/zou/basic/class1/A implements com/zou/basic/class1/IA  {
    
      // compiled from: A.java
      // access flags 0x19
      public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
    
      // access flags 0x1
      public <init>()V
       L0
        LINENUMBER 9 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.<init> ()V
       L1
        LINENUMBER 11 L1
        RETURN
       L2
        LOCALVARIABLE this Lcom/zou/basic/class1/A; L0 L2 0
        MAXSTACK = 1
        MAXLOCALS = 1
    
      // access flags 0x9
      public static add(II)I
       L0
        LINENUMBER 14 L0
        ILOAD 0
        ILOAD 1
        IADD
        IRETURN
       L1
        LOCALVARIABLE a I L0 L1 0
        LOCALVARIABLE b I L0 L1 1
        MAXSTACK = 2
        MAXLOCALS = 2
    
      // access flags 0x1
      public minus(II)I
       L0
        LINENUMBER 18 L0
        ILOAD 1
        ILOAD 2
        ISUB
        IRETURN
       L1
        LOCALVARIABLE this Lcom/zou/basic/class1/A; L0 L1 0
        LOCALVARIABLE a I L0 L1 1
        LOCALVARIABLE b I L0 L1 2
        MAXSTACK = 2
        MAXLOCALS = 3
    
      // access flags 0x1
      public plus(II)I
       L0
        LINENUMBER 22 L0
        ILOAD 1
        ILOAD 2
        IMUL
        IRETURN
       L1
        LOCALVARIABLE this Lcom/zou/basic/class1/A; L0 L1 0
        LOCALVARIABLE a I L0 L1 1
        LOCALVARIABLE b I L0 L1 2
        MAXSTACK = 2
        MAXLOCALS = 3
    
      // access flags 0x2
      private power(I)I
       L0
        LINENUMBER 26 L0
        ALOAD 0
        INVOKESPECIAL java/lang/Object.hashCode ()I
        ISTORE 2
       L1
        LINENUMBER 27 L1
        ILOAD 1
        ILOAD 1
        IMUL
        IRETURN
       L2
        LOCALVARIABLE this Lcom/zou/basic/class1/A; L0 L2 0
        LOCALVARIABLE a I L0 L2 1
        LOCALVARIABLE hash I L1 L2 2
        MAXSTACK = 2
        MAXLOCALS = 3
    
      // access flags 0x9
      public static main([Ljava/lang/String;)V
       L0
        LINENUMBER 32 L0
        BIPUSH 10
        BIPUSH 20
        INVOKESTATIC com/zou/basic/class1/A.add (II)I
        POP
       L1
        LINENUMBER 35 L1
        NEW com/zou/basic/class1/A
        DUP
        INVOKESPECIAL com/zou/basic/class1/A.<init> ()V
        ASTORE 1
       L2
        LINENUMBER 36 L2
        ALOAD 1
        BIPUSH 10
        INVOKESPECIAL com/zou/basic/class1/A.power (I)I
        POP
       L3
        LINENUMBER 38 L3
        ALOAD 1
        BIPUSH 20
        BIPUSH 10
        INVOKEVIRTUAL com/zou/basic/class1/A.minus (II)I
        POP
       L4
        LINENUMBER 40 L4
        ALOAD 1
        ASTORE 2
       L5
        LINENUMBER 41 L5
        ALOAD 2
        BIPUSH 10
        BIPUSH 20
        INVOKEINTERFACE com/zou/basic/class1/IA.plus (II)I
        POP
       L6
        LINENUMBER 43 L6
        ICONST_4
        ANEWARRAY java/lang/Integer
        DUP
        ICONST_0
        ICONST_1
        INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
        AASTORE
        DUP
        ICONST_1
        ICONST_2
        INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
        AASTORE
        DUP
        ICONST_2
        ICONST_3
        INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
        AASTORE
        DUP
        ICONST_3
        ICONST_4
        INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
        AASTORE
        INVOKESTATIC java/util/Arrays.asList ([Ljava/lang/Object;)Ljava/util/List;
        INVOKEINTERFACE java/util/List.stream ()Ljava/util/stream/Stream;
        INVOKEDYNAMIC accept()Ljava/util/function/Consumer; [
          // handle kind 0x6 : INVOKESTATIC
          java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
          // arguments:
          (Ljava/lang/Object;)V, 
          // handle kind 0x6 : INVOKESTATIC
          com/zou/basic/class1/A.lambda$main$0(Ljava/lang/Integer;)V, 
          (Ljava/lang/Integer;)V
        ]
        INVOKEINTERFACE java/util/stream/Stream.forEach (Ljava/util/function/Consumer;)V
       L7
        LINENUMBER 46 L7
        RETURN
       L8
        LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
        LOCALVARIABLE a Lcom/zou/basic/class1/A; L2 L8 1
        LOCALVARIABLE a1 Lcom/zou/basic/class1/IA; L5 L8 2
        MAXSTACK = 4
        MAXLOCALS = 3
    
      // access flags 0x100A
      private static synthetic lambda$main$0(Ljava/lang/Integer;)V
       L0
        LINENUMBER 44 L0
        GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
        ALOAD 0
        INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
       L1
        LINENUMBER 45 L1
        RETURN
       L2
        LOCALVARIABLE n Ljava/lang/Integer; L0 L2 0
        MAXSTACK = 2
        MAXLOCALS = 1
    }
    

    简书暂时不支持行号,所以相应的字节码在那个位置请各位看官自行查找。

    • invokestatic 调用静态方法add的时候是字节码中是
    L2
        LINENUMBER 36 L2
        ALOAD 1
        BIPUSH 10
        INVOKESPECIAL com/zou/basic/class1/A.power (I)I
        POP
    
    • invokevirtual 调用实例方法minus的时候的字节码:
    L3
        LINENUMBER 38 L3
        ALOAD 1
        BIPUSH 20
        BIPUSH 10
        INVOKEVIRTUAL com/zou/basic/class1/A.minus (II)I
        POP
    
    • invokespecial 调用minus的时候字节码:
    L2
        LINENUMBER 26 L2
        NEW com/zou/basic/class1/A
        DUP
        INVOKESPECIAL com/zou/basic/class1/A.<init> ()V
        ASTORE 2
    

    调用power方法的时候的字节码

    L2
        LINENUMBER 36 L2
        ALOAD 1
        BIPUSH 10
        INVOKESPECIAL com/zou/basic/class1/A.power (I)I
        POP
    

    power方法中调用Object.hashCode方法的字节码INVOKESPECIAL java/lang/Object.hashCode ()I
    从以上三个调用可以看出来调用构造方法、调用private方法及super调用的时候都是使用invokespecial指令

    • invokeinterface 调用接口方法plus的时候的字节码:INVOKEINTERFACE com/zou/basic/class1/IA.plus (II)I,注意如果不强转成接口IA去调用plus方法,那么其实使用的还是invokevirtual
    • invokedynamic Java7引入的字节码指令,是为JVM上的动态语言如Groovy,Scala, JRuby等,同时也是java8 lamda表达式的基础,有了这个指令,JVM才能更高效的产生动态的类,可以看到例子中lamda表达式其实是通过invokedynamic指令由LambdaMetafactory.metafactory动态生成了一个java.util.function.Consumer的类,invokedynamic并不是用来调用方法的,他是用来动态生成动态类的,不如lamda表达式背后其实就是一个类。
     INVOKEDYNAMIC accept()Ljava/util/function/Consumer; [
          // handle kind 0x6 : INVOKESTATIC
          java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
          // arguments:
          (Ljava/lang/Object;)V, 
          // handle kind 0x6 : INVOKESTATIC
          com/zou/basic/class1/A.lambda$main$0(Ljava/lang/Integer;)V, 
          (Ljava/lang/Integer;)V
        ]
    

    参考文献:
    http://stackoverflow.com/questions/30002380/why-are-java-8-lambdas-invoked-using-invokedynamic

    相关文章

      网友评论

          本文标题:Java各个方法调用浅析

          本文链接:https://www.haomeiwen.com/subject/qnmftxtx.html