注意
基本、標準和企業方案於 2025 年 3 月 17 日進入淘汰期。 如需詳細資訊,請參閱 Azure Spring Apps 淘汰公告。
本文適用於:✅ 基本/標準❎企業版
本文說明與 Java 記憶體管理相關的各種概念,以協助您瞭解 Azure Spring 應用程式所託管 Java 應用程式的行為。
Java 記憶體模型
Java 應用程式的記憶體具有多個部分,而且有不同方式可以分割。 本文說明由 Java 記憶體分割成的堆積記憶體、非堆積記憶體和直接記憶體。
堆積記憶體
堆積記憶體可儲存所有類別的執行個體和陣列。 每個 Java 虛擬機器 (JVM) 只有一個堆積區,由不同執行緒共用。
Spring Boot Actuator 可觀察堆積記憶體的值, Spring Boot Actuator 會將堆積值視為 jvm.memory.used/committed/max 的一部分。 如需詳細資訊,請參閱記憶體問題疑難排解工具的 jvm.memory.used/committed/max 區段。
堆積記憶體分為新世代和舊世代。 以下清單會說明這些詞彙及相關字詞。
年輕代 (young generation):所有新物件都會配置到年輕代,並隨時間老化。
- Eden 空間 (Eden space):新物件會放到 Eden 空間。
- 存活區:物件在經歷一次垃圾回收週期後,會從 Eden 移至存活區。 存留者空間可分成 s1 和 s2 等兩個部分。
舊世代 (old generation):也稱為續存空間。 已在存活區保留很長時間的物件,將會移至老年代。
在 Java 8 之前,另一個稱為永久代的區塊也是堆積記憶體的一部分。 從 Java 8 開始,永久世代由非堆積記憶體中的中繼空間取代。
非堆積記憶體
非堆積記憶體可分為以下部分:
從 Java 8 開始,取代永久代 (或 permGen) 的非堆積記憶體部分。 Spring Boot Actuator 會觀察此區段,並將其作為
jvm.memory.used/committed/max的一部分。 換句話說,jvm.memory.used/committed/max是堆積記憶體和非堆積記憶體前身 permGen 的總和。 先前的永久世代是由以下部分組成:- 中繼空間會儲存類別載入器所載入的類別定義。
- 壓縮類別空間主要適用於壓縮類別指標。
- 程式碼快取會儲存 JIT 編譯的原生程式碼。
至於其他記憶體 (例如執行緒堆疊),則不在 Spring Boot Actuator 的觀察範圍內。
直接記憶體
直接記憶體是由 java.nio.DirectByteBuffer 配置的原生記憶體,用於 nio 和 gzip 等協力廠商程式庫。
Spring Boot Actuator 不會觀察直接記憶體的值。
下圖摘要說明上一節所述的 Java 記憶體模型。
Java 記憶體回收
Java 垃圾回收(GC)有三個相關名詞,分別是「次要 GC」、「主要 GC」和「全域 GC」。 JVM 規格中並未明確定義這些詞彙。 在這裡,我們將「主要 GC」和「完整 GC」視為相同概念。
當 Eden 空間已滿時,會執行 Minor GC。 此作業會移除新生代中的所有死亡物件,並將存活的物件從 Eden 空間移到存活者空間的 s1,或從 s1 移動到 s2。
Full GC 或 major GC 會對整個堆積進行垃圾回收。 完整 GC 也可以回收中繼空間和直接記憶體等部分,這些部分只能由完整 GC 清除。
最大堆積大小會影響 minor GC 與 full GC 的頻率。 最大 metaspace 與最大 direct memory 大小會影響 full GC。
若您將堆積的大小上限設為較低的值,記憶體回收作業會更頻繁地執行,這會導致應用程式變慢一些,但較能限制記憶體的使用量。 當您將最大堆積大小設為較高值時,垃圾回收發生的頻率會降低,但這可能會增加記憶體不足 (OOM) 的風險。 如需詳細資訊,請參閱記憶體不足造成應用程式重新啟動的問題的記憶體不足問題類型一節。
Metaspace 和直接記憶體只能由 full GC 回收。 當 metaspace 或直接記憶體已滿時,就會發生 full GC。
Java 記憶體設定
下列各節說明 Java 記憶體設定的重要面向。
Java 容器化
應用程式會在容器環境中的 Azure Spring 應用程式內執行。 如需詳細資訊,請參閱將 Java 應用程式容器化。
重要 JVM 選項
您可以使用 JVM 選項來設定記憶體各個部分的大小上限。 您可以使用 Azure CLI 命令或透過 Azure 入口網站設定 JVM 選項。 如需詳細資訊,請參閱記憶體問題疑難排解工具的修改設定以修正問題一節。
以下清單說明 JVM 選項:
堆積大小設定
-
-Xms以絕對值設定初始堆積大小。 -
-Xmx以絕對值設定堆積大小上限。 -
-XX:InitialRAMPercentage以堆積大小/應用程式記憶體大小的百分比設定初始堆積大小。 -
-XX:MaxRAMPercentage以堆積大小/應用程式記憶體大小的百分比設定堆積大小上限。
-
直接記憶體大小設定
-
-XX:MaxDirectMemorySize以絕對值設定直接記憶體大小上限。 如需詳細資訊,請參閱 Oracle 文件的 MaxDirectMemorySize。
-
Metaspace 大小設定
-
-XX:MaxMetaspaceSize以絕對值設定中繼空間大小上限。
-
預設記憶體大小上限
以下各節說明如何設定預設的記憶體大小上限。
預設堆積大小上限
Azure Spring 應用程式會將 Java 應用程式的預設堆積記憶體大小上限設定在應用程式記憶體的 50% 至 80% 左右。 確切來說,Azure Spring 應用程式採用以下設定:
- 如果應用程式記憶體 < 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 Actuator 觀察)
- 中繼空間:每日使用量參考值為 50M 至 256M
- 程式碼快取
- 壓縮的類別空間
- 非觀察部分 (不在 Spring Boot Actuator 的觀察範圍):每日使用量參考值為 150M 至 250M。
- 執行緒堆疊
- GC、內部符號和其他
- 觀察到的部分 (由 Spring Boot Actuator 觀察)
- 直接記憶體:每日使用量參考值為 10M 至 200M。
以上資訊彙整為下圖, 灰色數字是記憶體每日使用量的參考值。
整體而言,設定記憶體的大小上限時,您應考量記憶體中各部分的使用量,而且所有大小上限的總和不應超過可供使用的記憶體總容量。
Java OOM
OOM 意指應用程式的記憶體不足, 可分為兩種不同的概念:容器 OOM 和 JVM OOM。 如需詳細資訊,請參閱記憶體不足造成應用程式重新啟動的問題。