博客 / 詳情

返回

【JVM調優】如何進行JVM調優?

JVM 調優是 Java 性能優化的核心環節,目的是 減少停頓時間(STW)、提高吞吐量、降低內存佔用,使應用運行更穩定高效。調優沒有萬能公式,需結合具體應用、硬件、GC 算法等進行。以下是系統化的調優指南:
調優核心步驟

  1. 明確目標
    問題可能出現的場景
    1.低延遲(如 Web 服務 <100ms GC 停頓)
    2.高吞吐量(如批處理,最大化 CPU 利用率)
    3.小內存佔用(容器環境)
    4.避免 OOM(內存泄漏排查優先)

先確定是哪個場景的問題

  1. 監控與診斷(先定位瓶頸!)
    JVM 內置工具:
    jps(進程號)→ jstat -gc <pid> 1000(每秒 GC 狀態)
    jmap -heap <pid>(堆概覽),jmap -histo:live <pid>(對象直方圖)
    jstack <pid>(線程棧,查死鎖/阻塞)
    可視化工具:
    JConsole、VisualVM、Eclipse MAT(內存分析)
    專業 Profiler:
    Arthas(實時診斷)、Async Profiler(低開銷)、JMC(Flight Recorder)
  2. 關鍵指標解讀
    GC 日誌(必備!啓動參數加 -Xlog:gc*,gc+heap=debug:file=gc.log):
  3. Young GC 頻率/耗時
  4. Full GC 觸發原因(Allocation Failure?Metadata GC Threshold?)
  5. 各區域內存回收效率(Eden/Survivor 晉升率)
    堆內存分佈:老年代增長是否異常?
    線程狀態:WAITING等待線程/BLOCKED 阻塞線程佔比

核心調優參數詳解

  1. 堆內存設置
    -Xms4g -Xmx4g # 初始堆=最大堆,避免動態擴容導致停頓
    -XX:MaxMetaspaceSize=512m # 元空間上限(防OOM)
    -Xmn1.5g # 新生代大小(G1通常不設,其他收集器建議佔堆1/3~1/2)
    AI寫代碼
    bash
    1
    2
    3
    2.選擇合適的 GC 算法(GC收集器)(JDK 17+ 推薦 G1)
    收集器 適用場景 關鍵參數
    G1 (默認) 平衡吞吐/延遲(JDK9+默認) -XX:MaxGCPauseMillis=200(目標停頓)
    -XX:G1HeapRegionSize=4m(Region大小)
    ZGC 超低延遲(TB級堆) -XX:+UseZGC
    -XX:ConcGCThreads=4
    (併發線程)
    Shenandoah 低延遲(與ZGC競爭) -XX:+UseShenandoahGC
    Parallel GC 高吞吐量(批處理) -XX:+UseParallelGC
    CMS (已廢棄) JDK14前老應用低延遲方案 不推薦新項目使用
  2. 優化分代比例(非G1時)
    -XX:SurvivorRatio=8 # Eden:Survivor=8:1(默認)
    -XX:NewRatio=2 # 老年代:新生代=2:1
    -XX:MaxTenuringThreshold=15 # 對象晉升老年代的年齡
    AI寫代碼
    bash
    1
    2
    3
    1.新生代和老年代的比例是1:2,新生代佔3分之一,老年代佔3分之2
    2.新生代裏面的Eden區和from,to區的比例是8:1:1

3.如果創建大量的短暫存活對象,可以增大新生代的空間
4.如果創建大量長久存活對象的時候, 可以增大老年代的空間
5.調整比例是為了優化垃圾回收的效率
6.但是新生代過大,會導致頻繁的垃圾回收,影響系統性能
7.如果老年代過大,會導致頻繁的full gc,也會影響系統性能
8.所以一般不建議調整比例

  1. 觸發 Full GC 的關鍵點
    FullGC是指會回收整個堆的內存,包含老年代、新生代、Metaspace等
    FullGC的觸發條件包括:

元空間不足 → -XX:MetaspaceSize=256m(初始大小)
System.gc() 調用 → -XX:+DisableExplicitGC(禁用)
老年代空間不足 → 檢查內存泄漏或增大堆。
JVM的GC(垃圾回收)既可以從新生代開始,也可以從老年代開始

JVM中的堆一般分為新生代、老年代和永久代(在Java 8及以後版本中,永久代被元空間取代)。GC主要有兩種類型:Minor GC和Major GC(或Full GC)123

Minor GC:主要發生在新生代,特別是Eden區和Survivor區之間的對象進行垃圾回收。當Eden區滿時,JVM會觸發Minor GC。Minor GC採用複製算法,將Eden和From Survivor區域中存活的對象複製到To Survivor區域,並將這些對象的年齡+1。如果對象在Survivor區中每熬過一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度(默認為15歲)時,就會被晉升到老年代中123

Major GC / Full GC:主要發生在老年代,當老年代空間不足時就會觸發Major GC。Major GC採用標記—清除算法,首先掃描一次所有老年代,標記出存活的對象,然後回收沒有標記的對象。Major GC的耗時比較長,因為要掃描再回收。Major GC會產生內存碎片,為了減少內存損耗,通常需要進行合併或者標記出來方便下次直接分配

高級調優技巧

  1. 避免大對象直接進老年代
    -XX:PretenureSizeThreshold=1m # >1MB 對象直接在老年代分配
    AI寫代碼
    bash
    1
  2. G1 優化建議
    -XX:InitiatingHeapOccupancyPercent=45 # 老年代佔用45%時啓動併發標記
    -XX:G1MixedGCLiveThresholdPercent=85 # 存活對象>85%的Region不回收
    AI寫代碼
    bash
    1
    2
  3. 減少 STW 停頓
    STW(Stop-The-World)是指在垃圾回收期間,為了準確地識別和清理不再使用的對象,垃圾收集器需要暫停所有用户線程的執行,這個暫停過程就被稱為STW

    -XX:+UseStringDeduplication # G1字符串去重(節省內存)
    -XX:+AlwaysPreTouch # 啓動時預分配物理內存(避免運行時缺頁)
    AI寫代碼
    bash
    1
    2

  4. 容器環境(Docker/K8s)
    -XX:+UseContainerSupport # 識別容器內存限制(必須!)
    -XX:MaxRAMPercentage=75.0 # 使用容器內存的75%
    AI寫代碼
    bash
    1
    2
    常見問題與解決
    現象 可能原因 解決方案
    Full GC 頻繁 內存泄漏 / 老年代過小 MAT分析堆快照 → 修復代碼 / 增大堆
    Young GC 耗時長 Survivor 空間不足 → 對象過早晉升 增大 -Xmn 或調整 -XX:SurvivorRatio
    請求延遲毛刺 GC 停頓導致 切換低延遲GC(ZGC/Shenandoah)
    Metaspace OOM 動態類加載過多(如Spring) 增大 -XX:MaxMetaspaceSize
    CPU 持續高負載 GC線程佔用 / 代碼死循環 top -Hp 查線程 → jstack 定位
    調優原則
    優先監控分析,而非盲目調參
    每次只改一個參數,觀察效果
    優先解決代碼問題(如內存泄漏)
    理解 GC 原理 > 死記參數
    生產環境灰度驗證
    📌 示例:電商應用調優參數(JDK17+G1)

-Xms8g -Xmx8g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=150
-XX:InitiatingHeapOccupancyPercent=40
-XX:+UseContainerSupport
-Xlog:gc*,gc+heap=debug:file=/logs/gc.log:time,uptime:filecount=5,filesize=100m
AI寫代碼
bash
1
2
3
4
5
6
調優是持續過程,建議結合 APM 工具(如 SkyWalking、Prometheus + Grafana)長期監控。遇到具體問題時,提供 GC 日誌和異常現象,能更精準定位原因。
————————————————

                        版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
                    

原文鏈接:https://blog.csdn.net/weixin_44891364/article/details/149222972

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.