Java 記憶體管理

注意

Azure Spring Apps 是 Azure Spring Cloud 服務的新名稱。 雖然服務有新的名稱,但是您暫時還是會在某些位置看到舊的名稱。我們正在致力更新螢幕擷取畫面、影片和圖表等資產。

本文適用於: ✔️基本/標準✔️企業

本文說明與 Java 記憶體管理相關的各種概念,以協助您了解裝載於 Azure Spring Apps 中的 Java 應用程式行為。

Java 記憶體模型

Java 應用程式的記憶體有數個部分,而且有不同方式可以分割元件。 本文討論 Java 記憶體,分為堆積記憶體、非堆積記憶體和直接記憶體。

堆積記憶體

堆積記憶體會儲存所有類別實例和陣列。 每個 Java 虛擬機 (JVM) 只有一個堆積區域,這會在線程之間共用。

Spring Boot 執行器可以觀察堆積記憶體的值。 Spring Boot 執行器會採用堆積值作為的一 jvm.memory.used/committed/max部分。 如需詳細資訊,請參閱工具中的 jvm.memory.used/committed/max 一節,以針對記憶體問題進行疑難解答。

堆積記憶分為 年輕一代老一代。 下列清單會說明這些詞彙,以及相關的詞彙。

  • 年輕一代:所有新物件都配置和年齡在年輕一代。

    • Eden 空間:新物件會配置在Eden空間中。
    • 倖存者空間:在倖存一個垃圾收集週期后,物件將從伊甸園移至倖存者空間。 倖存者空間可以分成兩個部分:s1 和 s2。
  • 老一代:也被稱為 終身空間。 長期留在倖存者空間中的物體將被轉移到舊一代。

在 Java 8 之前,另一個稱為 永久產生 的區段也是堆積的一部分。 從 Java 8 開始,永久產生是由非堆積記憶體中的中繼空間所取代。

非堆積記憶體

非堆積記憶體分成下列部分:

  • 從 Java 8 開始取代永久產生(或 permGen)的非堆積記憶體部分。 Spring Boot 執行器會觀察本節,並將它作為 的一 jvm.memory.used/committed/max部分。 換句話說, jvm.memory.used/committed/max 是堆積記憶體的總和和非堆積記憶體的前permGen部分。 前永久世代由下列部分組成:

    • Metaspace,其會儲存類別載入器所載入的類別定義。
    • 壓縮類別空間,這是針對壓縮類別指標。
    • 程序代碼快取,其會儲存 JIT 編譯的機器碼。
  • 其他記憶體,例如線程堆疊,未由 Spring Boot 執行器觀察到。

直接記憶體

直接記憶體是由 配置的 java.nio.DirectByteBuffer原生記憶體,用於 nio 和 gzip 等第三方連結庫。

Spring Boot 執行器不會觀察直接記憶體的值。

下圖摘要說明上一節所述的Java記憶體模型。

顯示 Java 記憶體模型的圖表。

Java 垃圾收集

Java 垃圾收集有三個詞彙:「次要 GC」、「主要 GC」和「完整 GC」。 這些詞彙在 JVM 規格中並未明確定義。 在這裡,我們認為「主要 GC」和「完整 GC」是相等的。

當 Eden 空間已滿時,次要 GC 會執行。 它移除了年輕一代中的所有死物體,並將活體物體從伊甸園空間移至倖存者空間的 s1,或從 s1 移到 s2。

完整 GC 或主要 GC 會在整個堆積中執行垃圾收集。 完整 GC 也可以收集中繼空間和直接記憶體等元件,這隻能由完整 GC 清除。

堆積大小上限會影響次要 GC 和完整 GC 的頻率。 中繼空間上限和直接記憶體大小上限會影響完整的 GC。

當您將堆積大小上限設定為較低的值時,垃圾收集會更頻繁地發生,讓應用程式變慢一點,但較好地會限制記憶體使用量。 當您將堆積大小上限設定為較高的值時,垃圾收集的頻率較低,這可能會造成更多的記憶體不足(OOM) 風險。 如需詳細資訊,請參閱應用程式重新啟動問題因記憶體不足問題所造成的記憶體不足問題類型一節

中繼空間和直接記憶體只能由完整 GC 收集。 當中繼空間或直接記憶體已滿時,就會發生完整的 GC。

Java 記憶體組態

下列各節說明 Java 記憶體設定的重要層面。

Java 容器化

Azure Spring Apps 中的應用程式會在容器環境中執行。 如需詳細資訊,請參閱 將 Java 應用程式容器化。

重要 JVM 選項

您可以使用 JVM 選項來設定記憶體每個部分的大小上限。 您可以使用 Azure CLI 命令或透過 Azure 入口網站 來設定 JVM 選項。 如需詳細資訊,請參閱 修改組態以修正問題 一節 ,以針對記憶體問題進行疑難解答。

下列清單描述 JVM 選項:

  • 堆積大小設定

    • -Xms 依絕對值設定初始堆積大小。
    • -Xmx 依絕對值設定堆積大小上限。
    • -XX:InitialRAMPercentage 依堆積大小/應用程式記憶體大小的百分比來設定初始堆積大小。
    • -XX:MaxRAMPercentage 依堆積大小/應用程式記憶體大小的百分比來設定堆積大小上限。
  • 直接記憶體大小設定

  • 中繼空間大小設定

    • -XX:MaxMetaspaceSize 依絕對值設定中繼空間大小上限。

默認記憶體大小上限

下列各節說明如何設定預設的最大記憶體大小。

預設最大堆積大小

Azure Spring Apps 會將 Java 應用程式的預設堆積記憶體大小上限設定為大約 50%-80% 的應用程式記憶體。 具體而言,Azure Spring Apps 會使用下列設定:

  • 如果應用程式記憶體 < 1 GB,預設的最大堆積大小會是 50% 的應用程式記憶體。
  • 如果 1 GB <= 應用程式記憶體 < 2 GB,則預設最大堆積大小會是 60% 的應用程式記憶體。
  • 如果 2 GB = 應用程式記憶體 < 3 GB<,則預設最大堆積大小會是 70% 的應用程式記憶體。
  • 如果 3 GB <= 應用程式記憶體,則預設的最大堆積大小會是 80% 的應用程式記憶體。

預設最大直接記憶體大小

如果未使用 JVM 選項設定最大直接記憶體大小,JVM 會自動將直接記憶體大小上限設定為 Runtime.getRuntime.maxMemory()傳回的值。 此值大致等於堆積記憶體大小上限。 如需詳細資訊,請參閱 JDK 8 VM.java 檔案

記憶體使用量配置

堆積大小會受到輸送量的影響。 基本上,在設定時,您可以保留預設的最大堆積大小,這讓其他部分具有合理的記憶體。

中繼空間大小取決於您的程式代碼複雜度,例如類別的數目。

直接記憶體大小取決於您的輸送量,以及使用 nio 和 gzip 等第三方連結庫。

下列清單描述 2 GB 應用程式的典型記憶體配置範例。 您可以參考此列表來設定記憶體大小設定。

  • 總記憶體 (2048M)
  • 堆積記憶體:Xmx 為 1433.6M(總記憶體的 70%)。 每日記憶體使用量的參考值為1200M。
    • 年輕一代
      • 倖存者空間 (S0, S1)
      • Eden 空間
    • 舊世代
  • 非堆積記憶體
    • 觀測部分(由 Spring Boot 執行器觀察到)
      • 中繼空間:每日使用量參考值為 50M-256M
      • 程序代碼快取
      • 壓縮類別空間
    • 未觀察到的部分(未由 Spring Boot 執行器觀察到):每日使用量參考值為 150M-250M。
      • 執行緒堆疊
      • GC、內部符號和其他
  • 直接記憶體:每日使用量參考值為 10M-200M。

下圖顯示相同的資訊。 灰色的數位是每日記憶體使用量的參考值。

2 GB 應用程式的典型記憶體配置圖表。

整體而言,設定記憶體大小上限時,您應該考慮記憶體中每個部分的使用方式,而且所有最大大小的總和不應超過可用記憶體總計。

Java OOM

OOM 表示應用程式記憶體不足。 有兩個不同的概念:容器 OOM 和 JVM OOM。 如需詳細資訊,請參閱 因記憶體不足問題所造成的應用程式重新啟動問題

另請參閱