這篇文章帶給大家的內容是關於Java虛擬機之棧幀的介紹(圖文),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
寫在前面的話:Java虛擬機是一門學問,是眾多Java大神們的傑作,由於我個人水平有限,精力有限,不能保證所有的東西都是正確的,這裡內容都是經過深思熟慮的,部分引用原著的內容,講的已經很好了,不在累述。當然在這裡,不可能所有細節都深層的分析,只講到一些比較重要的概念,由於對電腦組成原理理解不深,絕大部分只能採取黑盒理論來分析。
運行時的堆疊幀結構(什麼是堆疊幀?)
堆疊幀是虛擬機器進行呼叫和方法執行的資料結構,簡單的說棧幀其實就是JVM運行時資料區虛擬機器機棧(JVM Stack)的棧元素,,,每一個方法的執行和呼叫對應著一個棧幀。舉個簡單的例子,定義一個Stack ,這個Statck 中放入一些叫做棧幀的對象,這個對象包含了局部變數表,操作數棧,動態連結和方法返回地址等屬性。下面,我們來講一下棧幀這個物件的結構。
首先應該明白,一個執行緒中一個方法的呼叫鏈可能會很長,當中有很多方法都處在執行狀態,對於我們的執行引擎來說,只有位於棧頂的堆疊幀是有效的,這個叫做目前堆疊幀(Concurrent Satck Frame),與這個堆疊幀相關的方法稱作當前方法(Concurrent Method),對應的概念模型如下:
#透過堆疊框架的概念模型,接下來說說,堆疊框架這個物件的相關屬性,有什麼作用?資料結構是怎樣的
1.局部變數表(Local Variable Table)
它是一組變數的儲存空間,用於存放方法上和方法內部的變數。在Java編譯期就已完成局部變數表的最大容量分配,說的直白點,局部變數表就是儲存局部變數的表,用來儲存變數用的;其中它的容量用變數槽(Variable Slot,簡稱Slot)來衡量,Slot也是最小單位;相關資料參考周志明《深入理解Java虛擬機2版》P238;以下是相關類型的資料所佔用的記憶體空間
##從圖中可以看出,基本資料型別除了double和long分割成2個32位元(也就是2個Slot)進行儲存以外,即高位對齊方式;而其他型別都只佔用1個32位的Slot;另外
引用型別可能是32位也可能是64位,Java中沒有明確規定。那麼虛擬機器又是怎麼存取局部變數的呢?
虛擬機器透過索引定位法的方式使用局部變數表,索引值的範圍是從0到Slot的最大數量。在方法執行時,特別是執行實例方法時,那麼實例變數表的第0位索引預設是方法所屬的實例物件的參考「this」物件,接著是1到Slot參數變數到方法內部的局部變數。另外為了節省堆疊幀空間,局部變數的Slot是可以重複使用的,也就是說方法參數 方法內局部變數 ! =最大Slot數。由於Slot可以重複使用,不僅節省了空間的開銷,同時也對系統的垃圾回收起到意想不到的作用。參考P239
2.操作數堆疊(Operand Stack)
操作數堆疊是一個後入先出的堆疊(LIFO) ,基本原理和儲存方式和局部變數變一樣,32位元的資料型別用的棧的容量大小是1,64位元的就是2;方法執行的任何時候,操作數棧的深度都不會超過在max_statcks資料項中設定的最大值。參考P242,以下做個總結
1.棧楨剛建立時,裡面的操作數棧是空的。
2.Java虛擬機器提供指令來讓操作數堆疊對一些資料進行入棧操作,例如可以把局部變數表裡的資料、實例的欄位等資料入堆疊。
3.同時也有指令來支援出棧操作。
4.向其他方法傳參的參數,也存在操作數堆疊中。
5.其他方法傳回的結果,返回時存在操作數堆疊中。
6.堆疊幀中的部分操作數堆疊和上一個堆疊幀的局部變數變存在一定的重疊,主要是為了共享資料而存在。
3.動態連線(Dynamic Linking)
每個堆疊幀都包含一個指向運行時常數池中該堆疊幀所屬性方法的引用,持有這個引用是為了支援方法呼叫過程中的動態連接。在Class檔案的常數池中存有大量的 符號引用,字節碼中的方法呼叫指令就以常數池中指向方法的符號引用為參數。這些符號引用一部分會在類別載入階段或第一次使用的時候轉換為直接引用,這種轉換 稱為靜態解析。另外一部分將在每一次的運行期期間轉換為直接引用,這部分稱為動態連線。
4.方法回傳位址
當一個方法執行後,有兩種方式退出這個方法。第一種方式是執行引擎遇到任意一個方法回傳的字節碼指令,這時候可能會有回傳值傳遞給上層的方法呼叫者(呼叫目前方法的方法稱為呼叫者),是否有回傳值和傳回值的型別將根據遇到何種方法回傳指令來決定,這種退出方法方式稱為正常完成出口(Normal Method Invocation Completion)。
另外一種退出方式是,在方法執行過程中遇到了異常,而這個異常沒有在方法體內得到處理,無論是Java虛擬機器內部產生的異常,還是程式碼中使用athrow字節碼指令產生的異常,只要在本方法的異常表中沒有搜尋到匹配的異常處理器,就會導致方法退出,這種退出方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個方法使用異常完成出口的方式退出,是不會給它的呼叫都產生任何回傳值的。
無論採用何種方式退出,在方法退出之前,都需要返回到方法被調用的位置,程序才能繼續執行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執行狀態。一般來說,方法正常退出時,呼叫者PC計數器的值就可以當作回傳位址,堆疊幀中很可能會保存這個計數器值。而方法異常退出時,返回位址是 要透過異常處理器來確定的,堆疊幀中一般不會保存這部分資訊。
方法退出的過程實際上等同於把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的局部變數表和操作數棧,把返回值(如果有的話)壓入呼叫都堆疊幀的操作數棧中,呼叫PC計數器的值以指向方法呼叫指令後面的一條指令等。
5.附加資訊
虛擬機規格允許具體的虛擬機實作增加一些規範裡沒有描述的資訊到棧幀中,例如與高度相關的信息,這部分信息完全取決於具體的虛擬機實現。在實際開發中,一般會把動態連接,方法返回地址與其它附加資訊全部歸為一類,稱為棧幀資訊。
以上是Java虛擬機器之棧幀的介紹(圖文)的詳細內容。更多資訊請關注PHP中文網其他相關文章!