JVM 垃圾回收

最後更新: 2025-01-27 11:37:11 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

JVM 規範要求自動儲存體管理,但不規定回收器或堆積配置。因此,回收器的行為取決於 已部署的 JVM、版本、旗標、堆積大小與工作負載。

可達性與記憶體池 #

追蹤從 GC 根開始,包括活動執行緒的堆疊與暫存器、靜態欄位、JNI 控點,以及 VM 執行 階段結構。從根強可達的物件會保持存活。軟參照可能在記憶體壓力下被清除,弱參照不會 保留其參照物件,而幻象參照則透過 ReferenceQueue 支援事後協調。這些機制都不能 取代確定性的資源清理;請使用 try-with-resources,並僅將 Cleaner 作為安全網。終結 機制已遭棄用,並計畫移除。

Java 堆積儲存物件與陣列。在 HotSpot 中,元空間、程式碼快取、執行緒堆疊、直接緩衝區、 GC 記帳資料及原生程式庫會消耗堆積外的記憶體。-Xmx 只限制 Java 堆積,因此容器 限制必須為原生記憶體保留餘裕。記憶體池名稱與記帳方式取決於回收器及實作。

分代與配置 #

分代回收器利用許多配置物件會快速失效這項事實。新物件會配置到年輕代區域;存活物件 可能在倖存者區之間移動,並晉升至老年代區域。為避免每次年輕代回收都掃描所有老年代 物件,寫入屏障會將老年代指向年輕代的指標記錄在卡表或記憶集內。配置速率、存活集 大小、晉升速率以及可用的疏散空間,比單獨觀察堆積使用率更能預測壓力。

HotSpot 回收器的取捨 #

HotSpot 提供目標不同的回收器:

  • Serial 針對小型堆積,將實作負荷降至最低。
  • Parallel 透過平行的 stop-the-world 工作優先確保吞吐量。
  • G1 將堆積分割為多個區域,在平衡並行工作與疏散工作的同時,朝暫停時間目標運作。
  • ZGC 將大部分標記與重新定位工作並行執行,以達到較短暫停,但代價是屏障與並行 資源使用。
  • 若所選發行版提供 Shenandoah,它也會執行並行疏散,以縮短暫停時間。

回收器的可用性與預設值會隨 JDK 供應商及版本變更。不要將歷史預設值編碼到作業 指引中,而應使用 java -XX:+PrintCommandLineFlags -version 及其版本文件檢查已部署 的執行環境。

所有追蹤式回收器都需要協調點。根處理、重新標記及部分疏散工作可能會停止應用程式 執行緒(STW)。並行階段可減少暫停時間,但會消耗 CPU 與記憶體頻寬,需要屏障,並且 需要為重新定位保留可用空間。因此,較低的暫停目標可能降低吞吐量,或在配置壓力下失敗。

以證據為導向的調校 #

啟用統一記錄,並將其與應用程式延遲及資源指標關聯:

sh
java -Xlog:gc*,safepoint:file=gc.log:time,uptime,level,tags ...
java -XX:StartFlightRecording=filename=recording.jfr ...

使用 JFR 配置事件、jcmd、懷疑存在保留時產生的堆積傾印,以及 CPU/容器指標, 區分過度配置、持續增長的存活集、碎片化與原生記憶體耗盡。依照以下順序調校:

  1. 消除非預期的保留與配置熱點;
  2. 根據測得的存活集與配置突發量設定堆積大小;
  3. 針對測得的延遲與吞吐量目標選擇回收器;
  4. 只變更少量且有文件依據的目標;然後
  5. 重複具代表性的負載測量。

System.gc() 是要求,不是正確性機制,且可能引入干擾性工作。應用程式行為不得 依賴回收時機。

參考資料 #

評論

在 GitHub 上閱讀或評論(發表評論需要登入)