Java 虛擬機棧詳解分析
Java虛擬機棧
1. 定義
- 棧:線程運行時需要的內存空間,一個棧存在多個棧幀。棧具有先入後出,後入先出的特點。
- 棧幀:每個方法運行時需要的內存(局部變量表、操作數棧、動態鏈接和方法返回值等信息。),每次調用一個方法,便會將棧幀壓入棧中,方法執行完畢將棧幀從棧頂壓出
- 活動棧幀:指在棧頂的棧幀,既正在調用的方法,每個線程隻能有一個活動棧幀,對應著該線程正在調用的那個方法
現在我們用代碼來演示一下Java虛擬機如何將棧幀壓入及壓出棧中
public class Main { public static void main(String[] args) { method1(); } private static void method1() { method2(1, 2); } private static int method2(int a, int b) { int c = a + b; return c; } }
當我們運行Main函數時,jvm首先將棧幀Main壓入棧中,此視棧結構如圖所示
Main函數體中調用瞭method1方法,此時便會將棧幀1壓入棧中
method1方法體中調用瞭method2方法,這時jvm會將棧幀2壓入棧結構中,需要註意的是,前面我們提到瞭棧幀由 “局部變量表、操作數棧、動態鏈接和方法返回值”等信息組成,method2方法中擁有a,b兩個參數以及局部變量c和方法返回值
當method2方法執行完後,會將棧幀2從棧頂彈出
method1方法執行完畢後,將棧幀1彈出
依次順序直至線程被銷毀。
註意點
- 由於每個棧幀都會在方法調用完畢後被彈出,因此棧內存不需要進行垃圾回收
- 每個棧都是線程私有的,每個線程在創建的時候都會創建一個虛擬機棧,而由於物理內存是固定的,棧內存劃分得越大,可分配的線程數就越少
2. 棧的線程安全問題
局部變量是線程安全的
現在我們定義如下方法:
public static void method() { int a = 0; a++; }
我們在方法method中定義瞭一個局部變量a,並對其執行a++操作,現在假設我們有兩個線程同時調用瞭這個方法(棧幀),Java虛擬機會將該棧幀壓入各自線程的棧內存中,但由於局部變量表是線程私有的,所以兩個線程在同時調用這個棧幀後,a的值仍然都為1,故局部變量是線程安全的
方法參數和方法返回值不是線程安全
由於方法的參數和返回值均可被外部方法所引用,故在某個線程下某個方法可以更改另外一個方法的參數和返回值,故方法參數和方法返回值不是線程安全的
3. 棧內存溢出
由於程序執行時,虛擬機給每個棧分配的棧內存空間是固定的,所以在一些情況下有可能出現棧內存空間不足,導致溢出的情況,一般有兩種情況可能導致棧內存溢出
- 棧幀過大(較少出現)
- 棧幀過多(一般出現在遞歸時。沒有正確設置遞歸出口)
現在我們來解釋一下什麼時候會出現棧幀過多導致棧內存溢出,我們來假設某一個棧的內存空間大小是1024kb,現在有四個棧幀,每個棧幀的大小均為300kb,而 300 * 4 = 1200kb, 而 1200 > 1024 ,很顯然,現在這四個棧幀合起來的大小已經超過瞭這個棧的內存空間大小,這個時候就會出現棧內存溢出,也就是會報java.lang.StackOverflowError這個錯誤。
到此這篇關於Java 虛擬機棧詳解分析的文章就介紹到這瞭,更多相關Java 虛擬機棧內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!