您好,登錄后才能下訂單哦!
小編給大家分享一下 JVM中執(zhí)行引擎的案例分析,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
JVM中的執(zhí)行引擎在執(zhí)行java代碼的時候,一般有解釋執(zhí)行(通過解釋器執(zhí)行)和編譯執(zhí)行(通過即時編譯器產(chǎn)生本地代碼執(zhí)行)兩種選擇。
棧幀
定義:
棧幀是用于支持虛擬機進行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它位于虛擬機棧里面。
作用:
每個方法從調(diào)用開始到執(zhí)行完成的過程中,都對應(yīng)著一個棧幀在虛擬機棧里面從入棧到出棧的過程。
特點:
(1)棧幀包括了局部變量表,操作數(shù)棧等,到底是需要多大的局部變量表,多深的操作數(shù)棧是在編譯期確定的。因為一個棧幀需要分配多少內(nèi)存,不會受到程序運行期變量數(shù)據(jù)的影響。
(2)兩個棧幀之間的數(shù)據(jù)共享。在概念模型中,兩個棧幀是完全獨立的,但是在虛擬機的實現(xiàn)里會做一些優(yōu)化處理,令兩個棧幀出現(xiàn)一部分重疊。這樣在進行方法調(diào)用時,就可以共用一部分數(shù)據(jù),無須進行額外的參數(shù)復(fù)制傳遞。
(1)局部變量表
局部變量表是一組變量值存儲空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。
//方法參數(shù) max(int a,int b)
int a;//全局變量 void say(){ int b=0;//局部變量 }
局部變量和類變量(用static修飾的變量)不同
類變量有兩次賦初始值的過程:準備階段(賦予系統(tǒng)初始值)和初始化階段(賦予程序員定義的初始值)。所以即使在初始化階段沒有為類變量賦值也沒關(guān)系,它仍然有一個確定的初始值。
但局部變量不一樣,如果定義了,但沒有賦初始值,是不能使用的。
(2)操作棧
當一個方法剛剛開始執(zhí)行的時候,這個方法的操作數(shù)棧是空的,在方法的執(zhí)行過程中,會有各種字節(jié)碼指令往操作數(shù)棧中寫入和提取內(nèi)容,也就是出棧、入棧操作。
例如,計算:
int a=2+3
操作數(shù)棧中最接近棧頂?shù)膬蓚€元素是2和3,當執(zhí)行iadd指令時,會將2和3出棧并相加,然后將相加的結(jié)果5入棧。
(3)動態(tài)鏈接
Class文件的常量池中存有大量的符號引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號引用作為參數(shù)。這些符號引用分為兩部分:
靜態(tài)解析:在類加載階段或第一次使用的時候就轉(zhuǎn)化為直接引用。動態(tài)鏈接:在每一次運行期間轉(zhuǎn)化為直接引用。
(4)返回地址
當一個方法開始執(zhí)行后,只有兩種方式可以退出這個方法:正常退出、異常退出。無論采用何種退出方式,在方法退出之后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行。
當方法正常退出時
調(diào)用者的PC計數(shù)器作為返回地址。棧幀中一般會保存這個計數(shù)器值。
當方法異常退出時
返回地址是要通過異常處理器表來確定的。棧幀中一般不會保存這部分信息。
方法調(diào)用
方法調(diào)用是確定調(diào)用哪一個方法。
(1)解析
對“編譯器可知,運行期不可變”的方法進行調(diào)用稱為解析。符合這種要求的方法主要包括
靜態(tài)方法,用static修飾的方法私有方法,用private修飾的方法
(2)分派
分派講解了虛擬機如何確定正確的目標方法。分派分為靜態(tài)分派和動態(tài)分派。講解靜動態(tài)分派之前,我們先看個多態(tài)的例子。
Human man=new Man();
在這段代碼中,Human為靜態(tài)類型,其在編譯期是可知的。Man是實際類型,結(jié)果在運行期才可確定,編譯期在編譯程序的時候并不知道一個對象的實際類型是什么。
靜態(tài)分派:
所有依賴靜態(tài)類型來定位方法執(zhí)行版本的分派動作稱為靜態(tài)分派。它的典型應(yīng)用是重載。
public class StaticDispatch{ static abstract class Human{ } static class Man extends Human{ } static class Woman extends Human{ } public void say(Human hum){ System.out.println("I am human"); } public void say(Man hum){ System.out.println("I am man"); } public void say(Woman hum){ System.out.println("I am woman"); } public static void main(String[] args){ Human man = new Man(); Human woman = new Woman(); StaticDispatch sr = new StaticDispatch(); sr.say(man); sr.say(woman); } }
運行結(jié)果是:
I am human I am human
為什么會產(chǎn)生這個結(jié)果呢?
因為編譯器在重載時,是通過參數(shù)的靜態(tài)類型而不是實際類型作為判斷依據(jù)的。在編譯階段,javac編譯器會根據(jù)參數(shù)的靜態(tài)類型決定使用哪個重載版本,所以兩個對say()方法的調(diào)用實際為sr.say(Human)。
動態(tài)分派:
在運行期根據(jù)實際類型確定方法執(zhí)行版本的分派過程。它的典型應(yīng)用是重寫。
public class DynamicDispatch{ static abstract class Human{ protected abstract void say(); } static class Man extends Human{ @Override protected abstract void say(){ System.out.println("I am man"); } } static class Woman extends Human{ @Override protected abstract void say(){ System.out.println("I am woman "); } } public static void main(String[] args){ Human man = new Man(); Human woman = new Woman(); man.say(); woman.say(); man=new Woman(); man.say(); } }
運行結(jié)果:
I am man I am woman I am woman
這似乎才是我們平時敲的java代碼。對于方法重寫,在運行時才確定調(diào)用哪個方法。由于Human的實際類型是man,因此調(diào)用的是man的name方法。其余的同理。
動態(tài)分派的實現(xiàn)依賴于方法區(qū)中的虛方法表,它里面存放著各個方法的實際入口地址。如果某個方法在子類中被重寫了,那子類方法表中的地址將會替換為指向子類實現(xiàn)版本的入口地址,否則,指向父類的實現(xiàn)入口。
單分派和多分派:
方法的接收者與方法的參數(shù)統(tǒng)稱為方法的宗量,根據(jù)分派基于多少種宗量,分為單分派和多分派。
在靜態(tài)分派中,需要調(diào)用者的實際類型和方法參數(shù)的類型才能確定方法版本,所以其是多分派類型。在動態(tài)分派中,已經(jīng)知道了參數(shù)的實際類型,所以此時只需知道方法調(diào)用者的實際類型就可以確定出方法版本,所以其是單分派類型。綜上,java是一門靜態(tài)多分派,動態(tài)單分派的語言。
字節(jié)碼解釋執(zhí)行引擎
虛擬機中的字節(jié)碼解釋執(zhí)行引擎是基于棧的。下面通過一段代碼來仔細看一下其解釋的執(zhí)行過程。
public int calc(){ int a = 100; int b = 200; int c = 300; return (a + b) * c; }
第一步:將100入棧。
第二步:將操作棧中的100出棧并存放到局部變量中。后面的200,300同理。
第三步:將局部變量表中的100復(fù)制到操作數(shù)棧頂。
第四步:將局部變量表中的200復(fù)制到操作數(shù)棧頂。
第五步:將100和200出棧,做整型加法,最后將結(jié)果300重新入棧。
第六步:將第三個數(shù)300從局部變量表復(fù)制到棧頂。接下來就是將兩個300出棧,進行整型乘法,將最后的結(jié)果90000入棧。
第七步:方法結(jié)束,將操作數(shù)棧頂?shù)恼椭捣祷亟o此方法的調(diào)用者。
看完了這篇文章,相信你對 JVM中執(zhí)行引擎的案例分析有了一定的了解,想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。