JVM規範(四)Frames

Frames

一個frame是被用於存儲數據和部分結果的,以及執行動態鏈接、返回方法的值和分派異常。

frame在每次一個方法被調用時創建,在方法調用結束時銷燬,不管其完成的結果是正常的還是異常的。frame從創建此frame的線程的虛擬機棧分配。每個含有它自己的本地變量數組、它自己的操作棧和當前方法的類的運行時常量池的引用。

frame本地變量數組和其操作棧大小在編譯時確定,且同此frame相關聯的代碼一起被提供。因此frame數據結構的大小僅依賴於Java虛擬機的實現,些數據結構內存基於方法調用同時被分配。

在一個給定的線程中,任何時候都僅有一個frame(針對正在執行的方法的frame)處於活動狀態。這樣的frame被稱爲current frame,它的方法被稱爲current方法,它的方法所在的類被稱爲current class,對於局部變量和操作棧的操作通常會使用current frame的引用。

如果一個方法調用了另一個方法或者當前方法調用完成,那麼frame將不再是當前的(不再是current frame)。當一個方法被調用時,一個新的frame就會被創建並且在控制權轉換給這個新方法時成爲當前的(current)。當方法返回時,current frame回傳方法調用的結果,如果有的話,就傳給上一個frame(previous frame),這時current frame就會被廢棄,previous frame則變成當前的。

值得注意的是被一個線程創建的frame是屬於那個線程的私有的,不能夠再被其他線程引用。

Local Variables(局部變量)

每個frame都包含了一個局部變量的數組。一個frame的局部變量數組的長度是由編譯時確定的,以一個類或者接口連同與此frame相關聯的方法的代碼的二進制表示提供。

一個單一的局部變量其值類型可以是boolean byte char short int float reference 或者 returnAddress。一對局部變量其值可以是long或者double類型。

本地變量是通過編入索引中來編址的。每一個局部變量其索引爲0,如果一個整數在0與局部變理數組大小之間,均可作爲局部變量數組的索引。

long或者double類型的值獨佔相連兩個局部變量,這種值可能使用較小的索引編址。比如,存儲在局部變量數組中索引N處的的一個double類型的值實際佔據N和N+1兩處局部變量位置,但是不能使用N+1索引到此double值,可以在N+1位置存入局部變量,但這意味着作廢了N處的局部變量。

Java虛擬機並沒有要求N一定是偶數。更直觀的說,局部變量數組中的long或者double類型的值並不需要64bit對齊。實現者可自由的決定恰當的方式來通過使用兩個局部變量來表現這樣的值。

Java虛擬機使用局部變量在方法調用時傳遞參數,在類方法調用時,所有的參數都是以連續的局部變量的形式傳遞的,從0索引開始。在實例方法調用時,0索引處的局部變量通常用於傳遞一個對象(被調用方法所在的對象)的引用。隨後的所有參都是以連續的局部變量傳遞,從索引1開始。

Operand Stacks(操作數棧)

每個frame都包含了一個後進先出的棧,即爲它的Operand Stack。一個frame的Operand Stack的最大深度是由運行時決定的,其伴隨與frame相關聯的方法的代碼被提供。

在上下文環境中,有時我們使用current frame的Operand Stack作爲簡單的Operand Statck。

當frame被創建時,它的Operand Stack是空的。Java虛擬機提供指令來加載常量或者局部變量或者域的值到Operand Stack。別的虛擬機指令從Operand Stack提取操作數,對它們進行操作,然後將結果放回到Operand Stack。Operand Stack也用於預備傳遞給方法的參數和接收方法返回的結果。

例如,iadd指令同時添加兩個int類型的值,它要求被添加的int類型的值添加到Operand Stack的頂部。在執行時,兩個被添加的int類型值都會從Operand Stack彈出,如果是加法操作,那麼它們的和將會被返回且添加到Operand Stack的頂部。子計算指令可能內嵌到Operand Stack上。

Operand stack的每一入口都支持Java虛擬機的所有類型的值,包括long類型的值和double類型的值。

從Operand stack提取的值的操作都要基於它們的操作類型。例如,不能添加了兩個int類型的值,但隨後把它們當long類型對待,或者添加兩個float類型的值時使用iadd指令。少量的虛擬機指令(dup指令和swap指令)只把運行時數據區域的數據作爲原始數據值操作,而不考慮它們的類型,如此方式定義的虛擬機指令都不能用於修改或者破壞數據值。所有在Operand Stack上的操作的限制都地class文件驗證階段實施執行。

任何時候,一個Operand Stack都關聯着一個深度值,long或者double佔兩個單位的深度值,其他類型佔一個。

Dynamic Linking(動態鏈接)

每個frame都包含一個當前方法類型的運行時常量池引用,以支持方法代碼的動態鏈接。一個方法的class文件代碼指的是被調用的方法和通過符號引用被訪問的變量。動態鏈接將這些符號的方法引用轉換爲具體的方法引用。按需加載類以解決至今尚未定義的符號,轉換變量訪問爲這些變量的運行時位置相關聯的存儲結構的適當偏移量。

方法和變量的後期綁定在其他類中做出改變,以便一個方法的使用不會破壞這些代碼。

Normal Method Invocation Completion(正常方法調用結整束)

如果一個方法調用沒有引起拋出異常,包括虛擬機拋出或者執行了明確的throw語句,那麼即爲方法調用正常結束。如果當前方法調用正常結束,可能會返回給些方法調用都一個值,此種行爲發生在當被調用方法返回指令中的一個時,返回指令的選擇要依賴於被返回值的類型。

current frame就是這種情況下被用於恢復調用者的狀態,包括它的局部變量和Operand Stack(操作數棧),通過調用者的程序計數器(pc,program counter)恰當的增加來跳過方法引用指令。然後這個調用方法的方法繼續執行。

Abrupt Method Invocation Completion(意外方法調用結束)

一個方法調用意外結束即方法調用過程中發生了異常,而當前被調用方法未曾捕獲處理使得方法調用意外結束。意外方法調用結束有會給調用者返回值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章