更新於 2022/05/04閱讀時間約 4 分鐘

JVM stack and heap

    Java Visualizer
    使用 intelliJ IDE 開發工具的話, 建議可以安裝 java-visualizer 來觀察。
    首先關注 a, b 這 2 個變數, 於 call stack 區塊的顯示, 變數 a 是 primitive type 可以直接存放於 stack 空間, 原本以為 b 會有 Objects 紀錄, 原因是 Integer 在編譯為 byte code 時會變成 byteshort 存入 stack, 真是太神奇了 Jack。
    可以參照 StackOverflow 解釋, 但我沒有深究
    而變數 list 則是明顯的產生一個 Reference 參照到 Objects 裡面的 ArrayList 物件。

    JVM 什麼時候使用 stack 什麼時候使用 heap ?

    我個人覺得比較好理解的方式是, 從 StackOverflowErrorOutOfMemoryError 這兩個錯誤去認識 StackHeap
    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
    分享至
    成為作者繼續創作的動力吧!
    © 2024 vocus All rights reserved.