局部變量表:
保存函數的參數以及局部變量用的,局部變量表中的變量只在當前函數調用中有效,當函數調用結束后,隨着函數棧幀的銷毀,局部變量表也會隨之銷毀。
操作數
棧:
主要用於保存計算過程的中間結果,同時作為計算過程中變量臨時的存儲空間。只支持出棧入棧操作。
幀數據區:
棧幀需要一些數據來支持常量池解析、正常方法返回和異常處理等。在幀數據區中保存着訪問常量池的指針,方便程序訪問常量池。
此外,當函數返回或者出現異常時,虛擬機必須恢復調用者函數的棧幀,並讓調用者函數繼續執行下去。
對於異常處理,虛擬機必須有一個異常處理表,方便在發生異常的時候找到處理異常的代碼,因此異常處理表也是幀數據區中重要的一部分
棧上分配:優化技術
基本思想:
對於那些線程私有的對象,可以將它們打散分配在棧上,而不是分配在堆上。
分配在棧上的好處在於函數調用結束后可以自行銷毀,而不需要垃圾回收器的介入,從而提高系統的性能。
技術基礎:逃逸分析
逃逸分析的目的是,判斷對象的作用域是否有可能逃逸出函數體
/**
* Created by wb-xxd249566 on 2017/3/31.
*/
public class EscapeAnalysis {
private static User u;
public static void alloc(){
u = new User();
u.id=5;
u.name="xuexd";
}
}
對象
User u是類的成員變量,該字段有可能被任何線程訪問,因此屬於逃逸對象。
/**
* Created by wb-xxd249566 on 2017/3/31.
*/
public class EscapeAnalysis {
public static void alloc(){
User u = new User();
u.id=5;
u.name="xuexd";
}
}
在此處,
User以局部變量的形式存在,並且該對象並沒有被alloc()函數返回,或者出現了任何形式的公開,因此它未發生逃逸。
對於這種情況,虛擬機就有可能將User分配在棧上,而不是堆上。
/**
* Created by wb-xxd249566 on 2017/3/31.
*/
public class OnStackTest {
public static class User{
public int id = 0;
public String name = "";
}
public static void alloc(){
User u = new User();
u.id = 5;
u.name = "xuexd";
}
public static void main(String[] args) throws InterruptedException{
long b = System.currentTimeMillis();
for (int i=0;i<100000000;i++){
alloc();
}
long e = System.currentTimeMillis();
System.out.println(e-b);
}
}
運行使用參數:-server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-UseTLAB -XX:+EliminateAllocations
-server 在Server模式下,才可以啟用逃逸分析
-XX:+DoEscapeAnalysis 啟用逃逸分析技術
-Xmx10m -Xms10m 指定堆空間最大為10MB,顯然,如果對象在堆上分配,必然會引起大量的GC
-XX:+EliminateAllocations 開啟標量替換(默認打開),允許對象打散分配在棧上,比如對象擁有id和name倆個字段,那么這倆個字段將會被視為倆個獨立的局部變量進行分配
-XX:-UseTLAB 關閉TLAB
只有一次GC輸出,程序就執行完畢了,說明在執行過程中,User對象的分配過程被優化了。
將參數設為
-XX:-EliminateAllocations,即關閉標量替換后,則瘋狂打印GC:
說明
棧上分配依賴逃逸分析和標量替換的實現。
對於大量的零散小對象,棧上分配提供了一種很好的對象分配優化策略,棧上分配速度快,並且可以有效避免垃圾回收帶來的負面影響。
但由於和堆空間相比,棧空間較小,因此
對於大對象無法也不適合在棧上分配。
