首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JVM 垃圾回收核心原理深度解析:算法、收集器與實戰調優

     編輯:Java 綜合教學

一、垃圾回收的核心使命

JVM 運行時數據區中,**程序計數器、虛擬機棧、本地方法棧**隨線程生滅,自動回收無需 GC 介入。真正需要管理的是 **堆** 和 **方法區**(元空間),這裡存放對象實例與類元數據,空間有限且動態分配,必須依賴垃圾收集器回收無用對象,保障程序持續運行。

二、對象存活判定:從引用計數到可達性分析

1. 引用計數法—— 簡直但有缺陷

為對象添加計數器,引用+1,失效-1,計數為 0 即可回收。無法解決循環引用(A→B,B→A),主流 JVM 未採用。

2. GC Roots 可達性分析—— 標準算法

以 **GC Roots** 為起點,向下搜索形成引用鏈。不可達對象判定為可回收。

可作為 GC Roots 的對象:

  • 虛擬機棧中引用的對象(局部變量表)
  • 方法區中類靜態屬性/常量引用的對象
  • 本地方法棧中 JNI 引用的對象
  • 同步鎖持有的對象、JVM 內部對象(如 Class 對象、異常對象)

GC Roots 可達性分析示意圖

圖中 A、B、C、D 可達,F 不可達 → F 被回收。

三、四大經典回收算法

算法核心思想優點缺點適用場景
標記-清除標記不可達對象,統一清除實現簡單產生大量碎片,分配大對象困難老年代(配合空閒列表)
標記-整理標記後,移動存活對象一端,清理邊界外內存無碎片,分配效率高需移動對象、更新引用,耗時老年代、大對象存活率高區域
複製內存劃分兩塊,每次用一塊,GC 時存活對象複製到另一塊無碎片,分配極快(指針碰撞)內存利用率僅 50%新生代(存活率低)
分代收集新生代用複製,老年代用標記-清除/整理針對對象生命週期特性優化實現複雜通用堆(HotSpot 默認)

圖示演示

標記-清除前後:

標記清除前 標記清除後

標記-整理前後:

標記整理前 標記整理後

複製算法前後:

複製前 複製後

四、七大經典垃圾收集器

垃圾收集器關係圖 | 收集器 | 線程模式 | 算法 | 目標區域 | 核心特點 | 適用場景 | |--------|----------|------|----------|----------|----------| | **Serial** | 單線程 | 標記-整理 | 新生代/老年代 | 簡單高效,無線程切換開銷,但 **Stop-The-World (STW)** | Client 模式、單核、嵌入式 | | **Serial Old** | 單線程 | 標記-整理 | 老年代 | Serial 老年代版本,CMS 失敗備用 | 同上 | | **ParNew** | 多線程 | 複製 | 新生代 | Serial 多線程版,**唯一能與 CMS 配合**的新生代收集器 | Server 模式、需配合 CMS | | **Parallel Scavenge** | 多線程 | 複製 | 新生代 | **關注吞吐量**,自適應調節(`-XX:+UseAdaptiveSizePolicy`) | 批處理、科學計算、後台任務 | | **Parallel Old** | 多線程 | 標記-整理 | 老年代 | Parallel Scavenge 老年代版,**吞吐量優先組合** | 同上 | | **CMS** | 多線程 | 標記-清除 | 老年代 | **關注低停頓**,四階段:初始標記(STW)→並發標記→重新標記(STW)→並發清除 | Web 服務、響應敏感應用 | | **G1** | 多線程 | 標記-整理+複製 | 全堆 | **面向服務端**,Region 劃分、優先回收垃圾多區域、可預測停頓 | 大堆(>4GB)、多核、替代 CMS |

CMS 關鍵流程

  1. 初始標記 (STW):標記 GC Roots 直接關聯對象,極快
  2. 並發標記:遍歷對象圖,耗時長但與用戶線程並發
  3. 重新標記 (STW):修正並發階段變動的引用,較快
  4. 並發清除:清理垃圾,並發

缺陷: 碎片化、並發階段佔用 CPU、浮動垃圾、對 CPU 敏感。

G1 核心創新

  • 堆劃分為多個 **Region**(1-32MB),不再物理隔離新/老年代
  • **混合收集模式**:優先回收垃圾佔比高的 Region(Garbage First)
  • 四階段:初始標記(STW)→並發標記→最終標記(STW)→篩選回收(STW/並發)
  • 可指定目標停頓時間(`-XX:MaxGCPauseMillis`)

五、實戰調優核心參數

# 基礎堆設定
-Xms4g -Xmx4g -Xmn1.5g          # 固定堆大小,避免動態擴縮容抖動
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m

# 收集器組合選擇(二選一)
# 1. 吞吐量優先
-XX:+UseParallelGC -XX:+UseParallelOldGC
-XX:ParallelGCThreads=8
-XX:GCTimeRatio=19              # 吞吐量目標 95% (1/(1+19))

# 2. 低停頓優先 (JDK 8/11 推薦 G1)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200        # 目標停頓 200ms
-XX:G1HeapRegionSize=16m        # Region 大小
-XX:InitiatingHeapOccupancyPercent=45  # 觸發混合 GC 堆佔比

# 通用優化
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc.log
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/dump.hprof
-XX:+UseStringDeduplication     # G1 字符串去重 (JDK 8u20+)

六、常見誤區與避坑指南

  1. 盲目追求「零 GC」:適度 Young GC 是健康的,關注 Full GC 頻率與停頓。
  2. 參數照搬不驗證:業務模型不同(對象大小、存活率、分配速率),必須基於 GC 日誌分析調優。
  3. 忽略元空間:動態加載大量類(如 Groovy、Spring CGLIB)易導致 Metaspace OOM,需設定上限。
  4. 大對象直接進老年代:`-XX:PretenureSizeThreshold` 可調整,避免新生代頻繁複製大數組/對象。

垃圾回收是 JVM 自動內存管理的基石。深入理解算法與收集器特性,結合業務負載特徵,通過 GC 日誌持續觀測與調優,才能構建高穩定、高性能的 Java 應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved