JVM 調優是 Java 性能優化的核心環節,目的是 減少停頓時間(STW)、提高吞吐量、降低內存佔用,使應用運行更穩定高效。調優沒有萬能公式,需結合具體應用、硬件、GC 算法等進行。以下是系統化的調優指南:
調優核心步驟
- 明確目標
問題可能出現的場景
1.低延遲(如 Web 服務 <100ms GC 停頓)
2.高吞吐量(如批處理,最大化 CPU 利用率)
3.小內存佔用(容器環境)
4.避免 OOM(內存泄漏排查優先)
先確定是哪個場景的問題
- 監控與診斷(先定位瓶頸!)
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) - 關鍵指標解讀
GC 日誌(必備!啓動參數加 -Xlog:gc*,gc+heap=debug:file=gc.log): - Young GC 頻率/耗時
- Full GC 觸發原因(Allocation Failure?Metadata GC Threshold?)
- 各區域內存回收效率(Eden/Survivor 晉升率)
堆內存分佈:老年代增長是否異常?
線程狀態:WAITING等待線程/BLOCKED 阻塞線程佔比
核心調優參數詳解
- 堆內存設置
-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前老應用低延遲方案 不推薦新項目使用 - 優化分代比例(非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.所以一般不建議調整比例
- 觸發 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會產生內存碎片,為了減少內存損耗,通常需要進行合併或者標記出來方便下次直接分配
高級調優技巧
- 避免大對象直接進老年代
-XX:PretenureSizeThreshold=1m # >1MB 對象直接在老年代分配
AI寫代碼
bash
1 - G1 優化建議
-XX:InitiatingHeapOccupancyPercent=45 # 老年代佔用45%時啓動併發標記
-XX:G1MixedGCLiveThresholdPercent=85 # 存活對象>85%的Region不回收
AI寫代碼
bash
1
2 -
減少 STW 停頓
STW(Stop-The-World)是指在垃圾回收期間,為了準確地識別和清理不再使用的對象,垃圾收集器需要暫停所有用户線程的執行,這個暫停過程就被稱為STW-XX:+UseStringDeduplication # G1字符串去重(節省內存)
-XX:+AlwaysPreTouch # 啓動時預分配物理內存(避免運行時缺頁)
AI寫代碼
bash
1
2 - 容器環境(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