Java Visualizer
首先關注 a, b 這 2 個變數, 於 call stack 區塊的顯示, 變數 a 是 primitive type 可以直接存放於 stack 空間, 原本以為 b 會有 Objects 紀錄, 原因是 Integer 在編譯為 byte code 時會變成 byte 或 short 存入 stack, 真是太神奇了 Jack。
可以參照 StackOverflow 解釋, 但我沒有深究
而變數 list 則是明顯的產生一個 Reference 參照到 Objects 裡面的 ArrayList 物件。
JVM 什麼時候使用 stack 什麼時候使用 heap ?
我個人覺得比較好理解的方式是, 從 StackOverflowError 跟 OutOfMemoryError 這兩個錯誤去認識 Stack 跟 Heap。
StackOverflowError 觸發的條件在於產生大量的 stack memory 直到超過 JVM 設定的 stack 大小。簡單測試如下:
public static void main(String[] args) {
recursive(1);
}
// 這裡是一個無限遞迴, 每次遞迴都會使用一塊 stack 空間
private static void recursive(int i) {
System.out.println("print: " + i);
recursive(++i);
}
依據
Oracle 的文件描述, stack 可以是固定大小或是動態擴展的, 可以使用
java -Xss1M 這樣的設定去啟動 JVM, 宣告 stack 大小為 1Mb 空間。
This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation.
而 -Xss 的設定會依據 JVM 的環境, 或是 OS 的環境而有所差異, 這是 Oracle 提供的簡略參考對照表
https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/jrdocs/refman/optionX.html#wp1024112
而 OutOfMemoryError 的觸發條件, 源自於超過 Heap 使用上限, 搭配 -Xmx1M 很容易就達到。
public static void main(String[] args) {
// 由於限制了 1Mb 的 Heap 使用量, 所以根本無法產生大空間的 intArray
Integer[] intArray = new Integer[Integer.MAX_VALUE];
System.out.println(intArray);
}
總結
Stack 的空間用於 primitive type 或是用於指向 object reference, 依據官方文件描述, 每個 threads 都會有自己的 stack; 而 Heap 空間用於 Objects 實際上使用的空間。
若採用多執行緒的設計, Heap 空間就需要特別留意 OOM 問題, 特別是在 Microservice 的環境下, Replica 的設定沒有估計, 很容易就不小心吃掉大量記憶體, 導致同一個 Node 底下的服務因為記憶體不夠而不停重啟。
// JVM 使用的記憶體最大值
N-threads * Xms size = JVM Heap Size