1. 程式人生 > >棧幀-運算元棧,幀資料區,棧上分配

棧幀-運算元棧,幀資料區,棧上分配

運算元棧主要用於儲存計算過程的中間結果,同時作為計算過程中變數臨時的儲存空間。

運算元棧也是先進後出的資料結構。

這裡寫圖片描述

幀資料區:

除了區域性變量表和運算元棧,java棧還需要一些資料來支援常量池解析,正常方法返回和異常處理等,大部分java位元組碼指令需要進行常量池訪問,在幀資料區中儲存著訪問常量池的指標,方便程式訪問常量池。

當函式返回或者出現異常時,虛擬機器要恢復呼叫者函式的棧幀,並讓呼叫者函式繼續執行下去,對異常處理,虛擬機器必須有一個異常處理表,方便在發生異常的時候找到處理異常的程式碼,因此異常處理表也是幀資料區中重要的一部分。

這裡寫圖片描述

表示在位元組碼偏移量4~16位元組可能丟擲任意異常,如果遇到異常,則跳轉位元組碼偏移19處執行,當方法丟擲異常時,虛擬機器就會查詢類似的異常表來進行相應的處理,如果無法在異常表中找到合適的處理方法,則會結束當前函式的呼叫,返回呼叫函式,並在呼叫函式中丟擲相同的異常,並查詢呼叫函式 的異常表進行處理。

棧上分配:

棧上分配是java虛擬機器提供的一項優化技術,對於那些執行緒私有的物件(指不可能被其他執行緒訪問的物件),可以將它們打散分配在棧上,而不是分配在堆上。分配在棧上的好處是可以在函式呼叫結束後自行銷燬,而不需要垃圾回收器的介入,從而提高系統的效能。

棧上分配的一個技術基礎是進行逃逸分析。逃逸分析的目的是判斷物件的作用域是否有可能逃逸出函式體。如下程式碼顯示了一個逃逸的物件:

private static User user;
public static void alloc() {
user = new User();
user.id=1;
user.name=”test”;
}

物件User user是類的成員變數,該欄位有可能被任何執行緒訪問,因此屬於逃逸物件。如下程式碼顯示了一個非逃逸的物件:

public static void alloc() {
User user = new User();
user.id=1;
user.name=”test”;
}

在上述程式碼中,物件User以區域性變數的形式存在,並且該物件沒有alloc函式返回或者出現任何形式的公開,因此,它並未發生逃逸,所以這種情況,虛擬機器就有可能將User分配在棧上,而不是堆上。

只有在server模式下,才可以啟用逃逸分析。引數-XX:+DoEscapeAnalysis啟用逃逸分析,-XX:+EliminateAllocations開啟了標量替換(預設開啟),允許將物件打散分配在棧上,比如物件擁有id和name兩個欄位,那麼這兩個欄位將會被視為兩個獨立的區域性變數進行分配。

棧上分配依賴逃逸分析和標量替換的實現。

對於大量的零散小物件,棧上分配提供了一種很好的物件分配優化策略,棧上分配速度快,並且可以有效避免垃圾回收帶來的負面影響,但由於和堆空間相比,棧空間較小,因此對於大物件無法也不適合在棧上分配。