執行引擎是Java虛擬機最核心的組成部分之一,,全部的Java虛擬機的執行引擎都是一致的:
輸入:字節碼文件
處理:字節碼解析
輸出:執行結果java
在介紹虛擬機棧時就提到,每一個方法在執行的同時都會建立一個棧幀用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。棧幀中須要多大的局部變量表和多深的操做數棧在編譯代碼的過程當中已經徹底肯定,並寫入到方法表的Code屬性中。在活動的線程中,位於當前棧頂的棧幀纔是有效的,執行引擎運行的全部字節碼指令只針對當前棧幀進行操做。jvm
一切方法調用在Class文件裏面都是一個常量池中的符號引用。在類加載解析階段,可能會將其中一部分符號引用轉化爲直接引用,也有可能會在初始化階段以後再開始,取決於方法在運行以前是否有有肯定的調用版本,且在運行期間不變。
在Java中符合編譯器可知,運行期不可變的方法,主要包括靜態方法和私有方法兩大類,它們都不可能經過某些方式重寫其餘版本,所以它們都適合在類加載階段進行解析
JVM提供了5條方法調用字節碼:
invokestatic:調用靜態方法
invokespecial:調用實例構造器方法、私有方法和父類方法
invokevirtual:調用全部的虛方法
invokedynamic:運行時動態解析出調用點限定符所引用的方法,再執行該方法
只要能被invokestatic和invokespecial指令調用的方法,均可以在解析階段中肯定惟一的調用版本,它們在類加載時會把符號引用解析爲該方法的直接引用。
示例:
spa
public class Person {
public static void sayHello() {
System.out.println("hello world");
}
public static void main(String[] args) {
sayHello();
}
}
複製代碼
javap -verbose查看
public class Person {
public static void main(String[] args) {
new Person();
}
}
複製代碼
javap -verbose查看
經過invokestatic和invokespecial指令調用的方法稱爲非虛方法,與之相反,其餘方法稱爲虛方法(除去final方法),虛方法的調用是一個分派的過程,有靜態也有動態,可分爲靜態單分派、靜態多分派、動態單分派和動態多分派。線程
全部依賴靜態類型來定位方法執行版本的分派,靜態分派的典型應用是方法重載,靜態分派發生在編譯階段,所以靜態分派的動做不禁虛擬機執行。eg:3d
public class Person {
public void sayHello(Object obj){
System.out.println("hello Object");
}
public void sayHello(String str){
System.out.println("hello String");
}
public static void main(String[] args) {
Person p = new Person();
Object obj = new String();
p.sayHello(obj);
}
}
複製代碼
結果:code
hello Object
複製代碼
相對於變量obj,Object是其靜態變量,String是其實際變量,在編譯階段,Java編譯器會根據參數的靜態類型決定調用哪一個重載版本。用javap -verbose再看下
cdn
動態分派根據實際類型肯定方法執行版本對象
public class Person {
public static void main(String[] args) {
Object p = new Person();
Object obj = new String("比利時");
System.out.println(obj.toString());
System.out.println(p.toString());
}
}
複製代碼
輸出blog
比利時
jvm.Person@63ce0e18
複製代碼
同一個靜態類型,調用toString方法,結果徹底不一樣,緣由就是由於這兩個變量的實際類型不一樣。經過java -verbose 查看字節碼指令
繼承
《深刻理解Java虛擬機》