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這個錯誤,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/352203.html
標籤:java
上一篇:java---你真的認識例外嗎?
