博客 / 詳情

返回

垃圾回收機制:談談自己準備面試的時候一些理解

垃圾回收策略

1. JVM 垃圾回收策略機制描述

垃圾回收(GC)是JVM自動管理內存的一部分,用於釋放不再使用的對象所佔的內存空間。它的目標是:
(1)提升內存的使用率
(2)減少開發者手動管理內存的複雜性

2. 垃圾回收的主要策略

JVM的GC策略圍繞分代收集理論展開,任務不同時期的對象適合不同方式的回收

(1)分代收集:

  • 新生代垃圾回收採用 Minor GC(輕量級且頻繁)。
  • 老年代回收採用 Major GC 或 Full GC(耗時更長)。
  • 元空間:存儲類的元數據,取代了 JDK 8 之前的方法區。

(2)GC算法

  • 標記清理算法:缺點(產生大量碎片)
  • 複製算法:優點(適用於需要大量清理的空間,如新生代)
  • 標記整理算法:優點(減少內存碎片,適合老年代)
  • 分代收集算法:針對以上算法,針對不同代的特點進行優化
3. G1垃圾回收器原理

G1(Garbage First) 是 Java 7 引入的一種面向服務器的垃圾收集器,旨在提供低停頓、高吞吐的 GC 體驗。

(1)分區管理:

  • 將堆劃分為多個大小相等的域,每一個域可以是Eden,Survivor或者老年代
  • 動態分配域的角色,提升利用率

(2)並行和併發

  • GC工作線程可以併發執行
  • 一些耗時的操作(標記對象)在用户線程運行的時候併發執行。

(3)預測停頓時間

  • 允許用户設置最大停頓時間,並且儘量滿足

(4)增量刪除

  • 將回收任務分成小塊,以避免長時間的 Full GC 停頓

工作流程:

(1)初始標記階段

  • 標記於GC ROOT直接相關的對象
  • 時間短,與應用線程並行

(2)併發標記階段

  • 遍歷對象圖
  • 不會阻塞應用線程

(3)最終標記階段

  • 處理併發標記階段遺留的對象,重新標記
  • 短暫暫停

(4)篩選回收階段

  • 看那個區域(Region)的垃圾比較多,回收垃圾最多的區域
  • 執行標記整理,回收內存並減少碎片

適用場景:

大內存應用(6G以上)
對低延遲有強烈要求的應用

4.CMS與G1的選擇

  1. CMS(性價比只選)
  2. 適用於

    • 老年代對象增長比較緩慢
    • 時間響應敏感但是內存受限
    • 業務邏輯可以優化,減少GC壓力:優化對象生命週期的管理業務
  3. 不適用於

    • 老年代頻繁GC
    • 需要嚴格的控制時間
  4. G1(無奈只選,只能通過提升配置)
  5. 適用於:

    • 高併發/大內存(>8GB)
    • 老年代增長快而且不可控制
    • 對延遲敏感的應用(<200ms)
  6. 不適用於:

    • 內存成本受限
    • 小堆內存應用

5.業務代碼優化思路

  1. 減少短生命週期對象:

    • 避免頻繁的創建和銷燬
    • 優化使用對象池或者緩存
  2. 合理設計對象的生命週期

    • 儘量減少對象晉升老年代的頻率(通過調整JVM -XX:MaxTenuringThreshold),避免年輕人過早啃老
    • 對頻繁訪問的老年代對象,考慮設計為可複用或共享。
  3. 設置合理的內存分配

    • 根據應用特點調整 Eden 和 Survivor 區大小(-Xmn 或 -XX:SurvivorRatio)。
  4. 監控和調整JVM參數

    • 合理配置GC參數:例如 CMS 的 -XX:CMSInitiatingOccupancyFraction 或 G1 的 -XX:MaxGCPauseMillis。
    • 定期通過工具(如 JVisualVM、JProfiler)分析 GC 行為。

GC參數調優

  1. 主要參數
參數 説明
-XX:+UseG1GC 使用 G1 垃圾收集器
--XX:+UseConcMarkSweepGC 使用 CMS 垃圾收集器
-XX:NewRatio=ratio 新生代和老年代的比例,如2表示老年代是新生代的2倍
-Xmnsize 設置新生代的大小(絕對值)
-XX:SurvivorRatio=ratio 設置 Eden 區與 Survivor 區的大小比,例如 8 表示 Eden:Survivor = 8:1
-XX:MaxGCPauseMillis=ms (G1 特有)設置期望的 GC 最大停頓時間(毫秒)
-XX:CMSInitiatingOccupancyFraction=n (CMS 特有)設置 CMS 在老年代使用率達到 n% 時開始執行回收
-XX:+PrintGCDetails 輸出詳細 GC 日誌
-Xlog:gc* (JDK 9+)打印 GC 日誌,支持更靈活的格式,如 -Xlog:gc*=info:file=gc.log:uptime,level
  1. 調優步驟
    (1)確定GC的特性
  2. 吞吐量大優先:最大化CPU時間處理任務,允許GC時間可以長一點
  3. 低延遲優先:最小GC停頓時間, 最小化 GC 停頓時間,可能犧牲一定的吞吐量

(2) 選擇合適的GC

  • 小內存(<6G):優先使用 Parallel GC 或 CMS。
  • 大內存(>=6G):優點使用G1
  • 延遲敏感且大內存:選擇 G1 或 ZGC

(3) 對大小的劃分與比例

  • 堆大小(-Xms 和 -Xmx):根據系統內存和應用需求設置堆的初始大小和最大大小,一般兩者設為相同值,避免擴展造成性能損耗。
  • 新生代大小(-Xmn 或 -XX:NewRatio):新生代適合大部分短生命週期對象,分配更多空間可以減少 Minor GC。
  • Eden 與 Survivor 比例(-XX:SurvivorRatio):根據對象存活率,默認8適合大部份場景

(4) 調整回收參數

  • CMS 調優:

    • 提前啓動回收:-XX:CMSInitiatingOccupancyFraction=75(75% 老年代佔用時啓動 GC)。
    • 避免併發失敗:-XX:+UseCMSInitiatingOccupancyOnly 保證固定觸發條件。
  • G1 調優

    • 應用最大停頓時間:-XX:MaxGCPauseMillis=200 控制應用的最大停頓時間。
    • 回收目標區域大小:-XX:G1HeapRegionSize=16M 調整單個 Region 的大小,減少或增加 Region 數量。

(5) 持續監控和調整

  • 使用 APM 工具(如 Prometheus + Grafana 或 JVisualVM)監控 GC 性能。
  • 根據 GC 日誌和實際停頓時間不斷優化。

GC 日誌分析

  1. 如何獲取 GC 日誌

    • JDK8-:(-XX:PrintGCDetails)
    • JDK9+:(-Xlog:gc*:file=gc.log:time,uptime,level,tags)
  2. GC 日誌內容解析

(1) CMS日誌

2024-12-07T10:00:00.000+0000: [GC (Allocation Failure) [ParNew: 2048K->512K(6144K), 0.0056789 secs] 
[CMS: 10240K->8192K(20480K), 0.0123456 secs] 12288K->8704K(26624K), [Metaspace: 1024K->1024K(1024K)]]
  • [GC (Allocation Failure)]:GC 觸發原因(內存分配失敗)。
  • ParNew:新生代回收,Eden 區從 2048KB 回收至 512KB,耗時 0.0056 秒。
  • CMS:老年代回收,從 10240KB 減少到 8192KB,耗時 0.012 秒。
  • 總堆使用情況:從 12288KB 降至 8704KB。

(2) G1日誌

[2024-12-07T10:00:00.000+0000][info][gc,start    ] GC Pause (G1 Evacuation Pause) (young) 50M->30M(100M)
[2024-12-07T10:00:00.050+0000][info][gc,heap     ] Eden regions: 4->0(6)
[2024-12-07T10:00:00.050+0000][info][gc,heap     ] Survivor regions: 2->2(3)
[2024-12-07T10:00:00.050+0000][info][gc,heap     ] Old regions: 20->20
[2024-12-07T10:00:00.050+0000][info][gc,end      ] GC Pause (G1 Evacuation Pause) (young) 50M->30M(100M) 50ms
  • GC 類型:G1 Evacuation Pause (young) 表示年輕代回收。
  • 堆變化:堆從 50MB 降至 30MB,總堆大小為 100MB。
  • 區域變化:

    • Eden 區從 4 個區域回收為 0。
    • Survivor 區保持不變(2 個區域)。
    • 老年代區域未受影響。
  1. 分析重點:

(1)Minor GC 停頓時間

  • 查看年輕代 GC 停頓時間是否過高。
  • Eden區是否頻繁擴展,考慮調整 -Xmn 或 -XX:NewRatio。

(2)老年代使用情況

  • 如果頻繁觸發FullGC,檢查老年代的成長速度
  • 對 CMS,檢查 -XX:CMSInitiatingOccupancyFraction 是否過高。
  • 對 G1,檢查老年代分配的 Region 數量。

(3)對象的晉升

  • 如果對象過早晉升到老年代,調整 -XX:MaxTenuringThreshold。
  • 檢查 Survivor 區大小是否足夠。
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.