JVM學習筆記15 方法執行

bilibili-JVM學習筆記15 方法執行
The Java Virtual Machine Specification - Java SE 8 Edition

JVM學習筆記11 - Java字節碼初識
JVM學習筆記12 - 解讀筆記11中的attributes
JVM學習筆記13
JVM學習筆記14 異常

棧幀 stack frame

  • 棧幀是一種用於幫助虛擬機執行方法調用與方法執行的數據結構
    • 方法的局部變量表
    • 動態鏈接信息
      • 符號引用
      • 直接引用
    • 方法的返回地址
    • 操作數棧
  • 一個棧幀只會歸屬於某一個線程,所以不存在併發
    • 一個線程可能有多個棧幀
  • 局部變量表
  • solt
  • 靜態解析
    • 有些符號引用是在類加載階段或是第一次使用時就會轉換成爲直接引用
      1. 靜態方法
      1. 父類方法
      1. 構造方法
      1. 私有方法
    • 以上方法稱爲非虛方法,在類加載階段就可以將符號引用轉換爲直接引用
  • 動態鏈接
    • 另一些符號引用則是在每次運行期都會轉換爲直接引用,這體現爲 java 的多態性

invoke 指令

  • invokeinterface : 調用接口中的方法,實際上是在運行期決定的,決定到底調用實現該接口的哪個對象的特點方法;
  • invokestatic : 調用靜態方法
  • invokespecial :
    • 調用自己的私有方法
    • 調用自己的構造方法
    • 調用父類的方法(實例方法或構造方法)
  • invokevirtual : 調用虛方法(多態),運行期動態查找的過程
  • invokedynamic : jdk1.7 對動態語言的支持

public class InvokeTest1 {
    public static void test() {
        System.out.println("static");
    }

    public static void main(String[] args) {
        test();
    }
}
  public new_package.jvm.p51.InvokeTest1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lnew_package/jvm/p51/InvokeTest1;

  public static void test();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String static
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokestatic  #5                  // Method test:()V
         3: return
      LineNumberTable:
        line 14: 0
        line 15: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;

方法重載

package new_package.jvm.p51;

public class InvokeTest2 {

    public void test(Gradepa gradepa) {
        System.out.println("gradepa");
    }

    public void test(Father gradepa) {
        System.out.println("Fathet");
    }

    public void test(Son gradepa) {
        System.out.println("Son");
    }

    public static void main(String[] args) {
        Gradepa gradepa1 = new Father();
        Gradepa gradepa2 = new Son();

        InvokeTest2 main = new InvokeTest2();
        main.test(gradepa1);
        main.test(gradepa2);
    }

}

class Gradepa {
}

class Father extends Gradepa {
}

class Son extends Father {
}

// gradepa
// gradepa
  • 方法的靜態分派
    • Gradepa gradepa1 = new Father();
    • gradepa1 的靜態類型是 Gradepa ,gradepa1 的實際類型(真正指向的類型)是 Father
    • 變量的靜態類型是不會發生變化的,而變量的實際類型則是可以發生變化的(多態的一種體現),實際類型是在運行期方可確定。
  • 方法重載,是一種靜態行爲
  • 方法重寫,動態行爲

方法重寫 (p53)

  • 動態分派
    • invokevirtual 指令的多態查找流程
        1. 找到操作數棧頂的第一個引用的實際類型
        1. 尋找實際類型是否存在與調用類匹配的方法描述符,若存在且訪問權限校驗通過,則返回實際類型的該方法的直接引用
        1. 若不存在,則按繼承順序從下往上一次查找,查找到則調用
        1. 若一直查找不到,則拋出異常
    • 方法接收者:方法實際是由哪個對象調用的
package new_package.jvm.p51;

public class StackFrameTest1 {

    public static void main(String[] args) {

        Animal dog = new Dag();
        Animal cat = new Cat();

        dog.sleep();
        cat.sleep();

        dog = new Cat();
        dog.sleep();
    }
}

class Animal {
    public void sleep() {
        System.out.println("Animal is sleep");
    }
}

class Dag extends Animal {
    @Override
    public void sleep() {
        System.out.println("Dag is sleep");
    }
}

class Cat extends Animal {
    @Override
    public void sleep() {
        System.out.println("Cat is sleep");
    }
}
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class new_package/jvm/p51/Dag
         3: dup
         4: invokespecial #3                  // Method new_package/jvm/p51/Dag."<init>":()V
         7: astore_1
         8: new           #4                  // class new_package/jvm/p51/Cat
        11: dup
        12: invokespecial #5                  // Method new_package/jvm/p51/Cat."<init>":()V
        15: astore_2
        16: aload_1
        17: invokevirtual #6                  // Method new_package/jvm/p51/Animal.sleep:()V
        20: aload_2
        21: invokevirtual #6                  // Method new_package/jvm/p51/Animal.sleep:()V
        24: new           #4                  // class new_package/jvm/p51/Cat
        27: dup
        28: invokespecial #5                  // Method new_package/jvm/p51/Cat."<init>":()V
        31: astore_1
        32: aload_1
        33: invokevirtual #6                  // Method new_package/jvm/p51/Animal.sleep:()V
        36: return
      LineNumberTable:
        line 7: 0
        line 8: 8
        line 10: 16
        line 11: 20
        line 13: 24
        line 14: 32
        line 15: 36
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      37     0  args   [Ljava/lang/String;
            8      29     1   dog   Lnew_package/jvm/p51/Animal;
           16      21     2   cat   Lnew_package/jvm/p51/Animal;

重載與重寫的對比

  • 方法重載,是一種靜態行爲,編譯器就可以完全確定;
  • 方法重寫是動態的,是運行期行爲;

virtual method table

  • 針對方法調用動態分配的過程,虛擬機會在類的方法區建立一個虛方法表的數據結構(virtual method table, vtable);
  • 針對 invokeinterface 指令來說,虛擬機會建立一個接口方法表的數據結構(interface method table, itable);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章