Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

Tomcat JVM 參數調優避坑指南:CATALINA_OPTS vs JAVA_OPTS 關鍵差異與生產級配置模板

編輯:Linux 系統常見問題解答

前言:一個導致 OOM 的血淚教訓

許多開發者習慣在 catalina.sh 中直接修改 JAVA_OPTS 加入堆記憶體參數,卻不知這會連帶影響 shutdown.sh、version.sh 等所有 Tomcat 衍生進程。本文深度剖析 CATALINA_OPTS 與 JAVA_OPTS 的作用域差異,重現經典 OOM 故障現場,並提供生產環境驗證過的標準化配置模板。

一、故障複盤:為什麼關機也要 1G 記憶體?

錯誤配置

# catalina.sh 開頭直接加入
JAVA_OPTS="$JAVA_OPTS -server -Xms1G -Xmx1G -XX:+UseG1GC"

故障鏈路

  1. shutdown.sh 內部調用 catalina.sh stop
  2. catalina.sh stop 執行 org.apache.catalina.startup.Bootstrap stop
  3. Bootstrap 類啟動 JVM,**繼承了 JAVA_OPTS 的 1G 堆設定**
  4. 伺服器剩餘記憶體 < 200M,JVM 申請 1G 失敗 → OutOfMemoryError: Native memory allocation failed
  5. 關機失敗,只能 kill -9 強制終止
錯誤配置示意

二、核心差異:JAVA_OPTS vs CATALINA_OPTS

變數作用範圍適用場景繼承關係
JAVA_OPTS**所有**啟動 Tomcat 的 Java 進程JVM 通用參數:
- 記憶體模型選擇 (-client/-server)
- GC 日誌路徑
- JMX 監控埠
- 系統屬性 (-D)
被 CATALINA_OPTS 繼承
CATALINA_OPTS**僅** catalina.sh run/start 啟動的主進程應用層級參數:
- 堆大小 (-Xms/-Xmx)
- GC 演算法 (-XX:+UseG1GC)
- 應用專用 -D 參數
疊加在 JAVA_OPTS 之上

執行流程驗證

# catalina.sh 內部邏輯簡化
if [ "$1" = "start" ] || [ "$1" = "run" ]; then
    eval exec "\"$_RUNJAVA\"" "\"$JAVA_OPTS\"" "\"$CATALINA_OPTS\"" \
        -classpath "\"$CLASSPATH\"" \
        org.apache.catalina.startup.Bootstrap "$@" start
elif [ "$1" = "stop" ]; then
    eval exec "\"$_RUNJAVA\"" "\"$JAVA_OPTS\"" \
        -classpath "\"$CLASSPATH\"" \
        org.apache.catalina.startup.Bootstrap "$@" stop
    # 注意:stop 時**不包含 CATALINA_OPTS**
fi

三、正確配置:標準化模板

3.1 修改 bin/setenv.sh(推薦,不侵入原檔)

#!/bin/bash
# ==========================================
# Tomcat 生產環境 JVM 標準配置
# 檔案位置: $CATALINA_BASE/bin/setenv.sh
# ==========================================

# --- JAVA_OPTS: 通用 JVM 參數 ---
export JAVA_OPTS="-server \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8 \
-Duser.timezone=Asia/Taipei \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=$CATALINA_BASE/logs/heapdump.hprof \
-XX:ErrorFile=$CATALINA_BASE/logs/hs_err_pid%p.log \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9090 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-Djava.rmi.server.hostname=127.0.0.1"

# --- CATALINA_OPTS: 主進程專用參數 ---
export CATALINA_OPTS="-Xms2g -Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+ParallelRefProcEnabled \
-XX:+UnlockExperimentalVMOptions \
-XX:G1HeapRegionSize=16m \
-XX:InitiatingHeapOccupancyPercent=45 \
-XX:G1MixedGCLiveThresholdPercent=85 \
-XX:+DisableExplicitGC \
-Xlog:gc*:$CATALINA_BASE/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100m"

# --- 記憶體計算建議 ---
# 容器環境: -XX:MaxRAMPercentage=75.0
# 實體機: 保留 2-4G 給 OS/PageCache/Metaspace

3.2 關鍵參數解析(JDK 11+ G1GC 推薦值)

參數建議值說明
-Xms/-Xmx物理記憶體 50-70%設定相等避免動態擴縮抖動
-XX:+UseG1GC啟用JDK 9+ 預設,大堆低延遲首選
-XX:MaxGCPauseMillis100-200ms目標停頓時間,G1 會自動調整
-XX:G1HeapRegionSize16-32MB堆區塊大小,2G 堆建議 16MB
-Xlog:gc*統一日誌格式JDK 9+ 統一 GC 日誌,支援輪轉

四、容器化環境特別注意

# Dockerfile 最佳實踐
FROM tomcat:9-jre11
# 1. 複製配置
COPY setenv.sh /usr/local/tomcat/bin/
# 2. 容器記憶體感知 (JDK 10+)
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0"
# 3. 啟動命令
CMD ["catalina.sh", "run"]

⚠️ 避坑:Docker --memory=2g 限制容器記憶體,但 JVM 預設看宿主機記憶體。必須加上 -XX:MaxRAMPercentage 或顯式 -Xmx,否則 JVM 會嘗試分配宿主機級別的堆導致 OOM Kill。

五、驗證清單:配置生效確認

# 1. 檢查啟動參數
ps -ef | grep tomcat | grep -v grep
# 應看到 -Xms2g -Xmx2g -XX:+UseG1GC 在 catalina 進程中

# 2. 確認關機進程無堆參數
./shutdown.sh
ps -ef | grep Bootstrap
# stop 進程僅含 JAVA_OPTS,無 -Xmx

# 3. GC 日誌輪轉驗證
ls -lh $CATALINA_BASE/logs/gc.log*

# 4. JMX 連線測試
jconsole localhost:9090

六、常見誤區總結表

誤區後果正確做法
在 JAVA_OPTS 設 -Xmx關機/工具進程 OOM移至 CATALINA_OPTS
未設 HeapDumpOnOOMOOM 現場無法分析加入 JAVA_OPTS
容器未設 MaxRAMPercentage容器被 OOM Kill設 70-80%
生產用 ParallelGC大堆停頓不可控改 G1GC/ZGC
直接改 catalina.sh升級覆蓋、難維護用 setenv.sh

JVM 參數調優不是玄學,而是「作用域隔離 + 容量規劃 + 可觀測性」三位一體。把 CATALINA_OPTS 留給主進程,把 JAVA_OPTS 留給平台能力,把 GC 日誌留給事後複盤。這樣配置的 Tomcat,重啟不再驚心動魄,OOM 也不再無從下手。

Copyright © Windows教程網 All Rights Reserved