一、為什麼需要 JVM 調優?
默認 JVM 參數適用於小型應用,但在以下場景中往往表現不佳:
- 高併發 Web 服務(如電商、支付系統)
- 大數據處理(如 Spark、Flink 任務)
- 實時性要求高的交易系統
- 內存佔用大或 GC 停頓頻繁的應用
常見問題表現:
- 頻繁 Full GC,響應時間飆升
OutOfMemoryError或StackOverflowError- 系統吞吐量低,CPU 利用率異常
- 應用啓動慢,預熱時間長
二、JVM 內存結構回顧(HotSpot)
調優前必須清楚內存佈局:
JVM 內存 = 堆(Heap) + 非堆(Non-Heap)
堆(線程共享):
├── 新生代(Young Gen)
│ ├── Eden
│ └── Survivor (S0, S1)
└── 老年代(Old Gen)
非堆:
├── 方法區(Metaspace,JDK 8+)
├── 棧(每個線程私有)
├── 程序計數器
└── 本地方法棧
💡 重點調優區域:堆內存 和 垃圾回收器(GC)
三、核心調優目標
|
目標 |
説明 |
|
降低 GC 頻率 |
減少 Stop-The-World(STW)停頓 |
|
縮短 GC 停頓時間 |
提升響應速度(尤其對延遲敏感系統) |
|
提高吞吐量 |
單位時間內處理更多請求 |
|
避免內存溢出 |
合理分配內存,防止 OOM |
四、關鍵 JVM 參數詳解
1. 堆內存設置
-Xms4g # 初始堆大小(建議 = -Xmx,避免動態擴容)
-Xmx4g # 最大堆大小
✅ 生產建議:-Xms == -Xmx,防止運行時擴容導致性能抖動。
2. 新生代大小
-Xmn2g # 設置新生代大小(推薦為堆的 1/3 ~ 1/2)
-XX:NewRatio=2 # 老年代:新生代 = 2:1(默認值,一般不需改)
-XX:SurvivorRatio=8 # Eden:S0:S1 = 8:1:1(默認)
📌 對象大多“朝生夕死”,適當增大新生代可減少 Minor GC 次數。
3. 垃圾回收器選擇(JDK 8 vs JDK 17+)
JDK 8 常用組合:
|
場景
|
推薦 GC
|
參數
|
|
吞吐優先(批處理)
|
Parallel GC(默認)
|
|
|
延遲敏感(Web 服務)
|
CMS(已廢棄)
|
|
|
平衡型
|
G1(JDK 8u40+ 支持)
|
|
JDK 11+ 推薦:
|
GC
|
特點
|
適用場景
|
|
G1 |
可預測停頓、大堆友好
|
通用首選(4GB~幾十GB)
|
|
ZGC |
停頓 < 10ms,支持 TB 級堆
|
超低延遲(JDK 11+ 實驗,JDK 15+ 生產可用)
|
|
Shenandoah |
類似 ZGC,Red Hat 主導
|
低延遲替代方案
|
✅ 現代生產環境推薦(JDK 11+):
# G1 示例(平衡型)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 目標最大停頓時間(ms)
-XX:G1HeapRegionSize=16m # 堆分區大小(可選)
# ZGC 示例(超低延遲)
-XX:+UseZGC
-Xmx16g
4. 元空間(Metaspace)調優(JDK 8+)
-XX:MetaspaceSize=256m # 初始元空間大小
-XX:MaxMetaspaceSize=512m # 最大元空間(防無限增長)
⚠️ 不設上限可能導致系統內存耗盡(尤其動態生成類的場景,如 Spring、Groovy)。
5. GC 日誌開啓(排查必備!)
# JDK 8
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/path/to/gc.log
# JDK 9+
-Xlog:gc*:file=/path/to/gc.log:time,uptime,level,tags