三大 OOM 類型速查
| 異常 | 區域 | 典型原因 | 核心參數 |
Java heap space | 堆 | 對象過多/大、洩漏、堆太小 | -Xms, -Xmx |
PermGen space (JDK7) | 永久代 | 類過多、動態代理、熱部署 | -XX:PermSize, -XX:MaxPermSize |
Metaspace (JDK8+) | 元空間 | 同上,受本地內存限制 | -XX:MetaspaceSize, -XX:MaxMetaspaceSize |
unable to create new native thread | 線程/本地內存 | 線程數過多、堆佔用過大擠佔本地內存 | -Xss, -Xmx, 系統 ulimit |
1. 堆內存溢出
原理
- JVM 啟動初始堆
-Xms(物理內存 1/64),最大堆 -Xmx(1/4)。
- 98% 時間做 GC 且可用堆 <2% → 拋 OOM。
- 建議
-Xms = -Xmx 避免震蕩,-Xmn(新生代)約 1/4 -Xmx。
參數模板(1G/2G/4G 機器)
# 1G 內存
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxPermSize=128m -XX:MaxNewSize=256m -Djava.awt.headless=true"
# 2G 內存 (64位)
JAVA_OPTS="-server -Xms1024m -Xmx1536m -XX:PermSize=128M -XX:MaxPermSize=256m -XX:MaxNewSize=256m"
# 4G+ 生產通用
JAVA_OPTS="-server -Xms2g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Xmn512m"
配置生效方式
startup.sh/bat 啟動:修改 catalina.sh/bat 頭部 JAVA_OPTS 或 CATALINA_OPTS。
- Windows 服務啟動:修改註冊表
HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Tomcat Service Manager\TomcatX\Parameters\JavaOptions 追加參數。
2. 永久代/元空間溢出
- 存放類元數據、常量池(JDK7)、靜態變量。
- GC 不主動清理,動態生成類(CGLIB、JSP、JDK Proxy)易爆。
- 方案:增大
PermSize/MaxPermSize (JDK7) 或 MetaspaceSize/MaxMetaspaceSize (JDK8);將公共 JAR 移至 tomcat/shared/lib 減少重複加載。
3. 無法創建本地線程
公式
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / ThreadStackSize = MaxThreads
- 32 位進程限 2G 用戶空間;分配 1.5G 堆 → 剩 500M 扣除系統 DLL 後約 400M。
- JDK 1.5+ 默認棧 1M/線程 → 理論極限 ~400 線程。
- **反直覺結論**:**減小 -Xmx 反而能創建更多線程**。
實際案例
1.5G JVM: (2G - 1.5G - 120M) / 1M ≈ 380 threads
1.0G JVM: (2G - 1.0G - 120M) / 1M ≈ 880 threads
調優建議
- 減小
-Xmx、-Xss(如 256k)。
- 遷移 64 位 JVM 突破 2G 限制。
- 檢查線程池配置,避免無限創建。
運行時內存監控代碼
System.out.println("MAX: " + Runtime.getRuntime().maxMemory()/1024/1024 + "M");
System.out.println("TOTAL: " + Runtime.getRuntime().totalMemory()/1024/1024 + "M");
System.out.println("FREE: " + Runtime.getRuntime().freeMemory()/1024/1024 + "M");
maxMemory() = -Xmx 值。
totalMemory() = 已向 OS 申請堆大小(含已用+空閒)。
freeMemory() = totalMemory() 中未使用部分。
HotSpot 分代堆結構參數
- 新生代/老年代比:
-XX:NewRatio=3 (1:3)
- 新生代固定:
-Xmn64m 或 -XX:NewSize=64m -XX:MaxNewSize=64m
- 永久代:
-XX:PermSize=32m -XX:MaxPermSize=64m