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

JVM 深度解析(六):垃圾回收機制全景圖

     編輯:Java 綜合教學

從手動管理到自動回收:GC 的演進與核心

C/C++ 開發者擁有內存管理的至高權力,卻也背負著內存洩漏與溢出的沉重擔子。Java 程序員則受惠於 JVM,將內存管理交給垃圾回收器(GC)。本文系統梳理 對象存活判定、回收算法、收集器實現、內存分配策略、核心參數 五大維度,構建 GC 完整知識體系。

一、如何判斷對象「已死」?

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

為每個對象添加計數器,引用+1,失效-1,歸零即回收。主流 JVM 未採用,因無法解決 循環引用 問題:

MyObject obj1 = new MyObject();
MyObject obj2 = new MyObject();
obj1.ref = obj2;
obj2.ref = obj1;
obj1 = null; obj2 = null; // 計數器永不歸零,實際已不可用

2. 可達性分析法—— JVM 的標準選擇

以 GC Roots(虛擬機棧引用、方法區靜態屬性/常量引用、本地方法棧 JNI 引用等)為起點,向下搜索形成引用鏈。不可達對象 ≠ 立即回收,需經歷 兩次標記 確認無「復活」可能(finalize() 重新建立引用)才真正判死刑。

可達性分析示意
藍色:存活對象;白色:判定可回收對象

二、主流垃圾收集算法

算法核心思想優點缺點適用場景
標記-清除標記垃圾 → 統一清除基礎簡單效率低;產生大量不連續碎片老年代(配合整理)
複製內存劃兩塊,每次用一塊,存活對象複製到另一塊,再整塊清理無碎片,分配極快(指針碰撞)浪費 50% 空間新生代(存活率低)
標記-整理標記後,存活對象向一端移動,清理邊界外內存無碎片,空間利用率高移動對象開銷大老年代(存活率高)
分代收集按對象壽命劃分新生代/老年代,分別採用最適算法綜合效能最優實現複雜所有商業 JVM 默認
標記-清除 複製算法 標記-整理

三、HotSpot 經典垃圾收集器(JDK 1.6 u22 基準)

收集器 = 算法的具體實現,不同廠商、版本差異極大。下表梳理核心收集器特性與組合:

收集器代別算法線程特點/優勢典型組合
Serial新生代複製單Client 模式默認,簡單高效+ Serial Old
ParNew新生代複製多Serial 多線程版,縮短停頓+ Serial Old / CMS
Parallel Scavenge新生代複製多關注 吞吐量,適合後台計算+ Serial Old / Parallel Old
Serial Old老年代標記-整理單JDK 1.5 前 Parallel Scavenge 搭檔+ Parallel Scavenge
Parallel Old老年代標記-整理多多核優勢,配合 Parallel Scavenge 吞吐量最大化+ Parallel Scavenge
CMS老年代標記-清除多關注 最短停頓,並發收集,適合高交互ParNew + CMS + Serial Old(備用)
CMS 關鍵細節:初始標記/重新標記需 STW,並發標記/清除不停頓;預設老年代 68% 觸發 Full GC;碎片整理參數 -XX:+UseCMSCompactAtFullCollection;G1 於 JDK 1.7 正式發布,打破代別概念,此處不再展開。

四、內存分配與回收策略

Java 堆結構劃分

JVM 結構 內存分配 堆中各代分布
  • 新生代:Eden + Survivor0 + Survivor1(默認 8:1:1)。新對象優先 Eden,Minor GC 複製存活對象在 Survivor 間輪換,年齡達閾值(默認 15)進老年代。-XX:MaxTenuringThreshold 控制。
  • 老年代:長壽對象聚集地,Major/Full GC 頻率低。大對象(長字串/大數組)可直接進老年代(-XX:PretenureSizeThreshold)。Card Table 記錄老年代→新生代引用,加速 Minor GC 掃描。
  • 永久代/元空間:存放 Class/Metadata。常量池、無用類可回收(需滿足三條件),-Xnoclassgc 可關閉類卸載。

五、核心 GC 參數速查表

類別參數含義
堆設置-Xms初始堆大小
-Xmx最大堆大小
-XX:NewSize新生代大小
-XX:NewRatio新生代:老年代 比例(如 3 即 1:3)
-XX:SurvivorRatioEden:Survivor 比例(如 3 即 3:2,單 Survivor 佔 1/5)
-XX:MaxPermSize永久代大小(JDK 8 後為元空間)
收集器選擇-XX:+UseSerialGC串行收集器
-XX:+UseParallelGC並行收集器(吞吐量優先)
-XX:+UseParallelOldGC並行老年代收集器
-XX:+UseConcMarkSweepGC並發收集器(停頓優先)
日誌輸出-XX:+PrintGCDetailsGC 詳細信息
-XX:+PrintHeapAtGCGC 前後堆詳情
-XX:+PrintGCTimeStampsGC 時間戳
-XX:+PrintTenuringDistribution對象年齡分佈
-XX:+HandlePromotionFailure老年代分配擔保
並行調優-XX:ParallelGCThreads並行收集線程數
-XX:MaxGCPauseMillis最大停頓時間(Parallel Scavenge)
-XX:GCTimeRatio吞吐量比例 1/(1+n)
CMS 調優-XX:+CMSIncrementalMode增量模式(單 CPU)
-XX:CMSInitiatingOccupancyFraction觸發 Full GC 老年代佔比(默認 68%)

結語

GC 是 JVM 性能調優的核心戰場。理解「判定→算法→收集器→分配策略→參數控制」完整鏈路,才能從容應對線上 OOM、長停頓、吞吐率低等疑難雜症。下一講我們將實戰 GC 日誌分析與調優案例,敬請期待。

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