生產環境 Tomcat 內存溢出(OOM)是 Java 運維高頻痛點。本文系統梳理三大核心 OOM 類型——Heap Space、PermGen/Metaspace、Unable to create new native thread——的成因、診斷路徑與差異化調優策略,助你從根因解決而非盲目加大內存。
| 異常信息 | 內存區域 | 核心成因 | 典型場景 |
|---|---|---|---|
OutOfMemoryError: Java heap space | 堆 | 對象分配速率 > GC 回收速率,或存在內存洩漏 | 大數據查詢、集合無限增長、緩存無淘汰 |
OutOfMemoryError: PermGen space (JDK<8)OutOfMemoryError: Metaspace (JDK≥8) | 永久代/元空間 | 類加載量過大,GC 不回收(PermGen)或元空間上限未設 | JSP 預編譯、熱部署頻繁、框架動態生成類、大量第三方庫 |
OutOfMemoryError: unable to create new native thread | 本機內存(非堆) | 進程虛擬地址空間耗盡,無法分配線程棧 | 高併發線程池、線程洩漏、堆設過大擠佔本機內存 |
JVM 判斷:若 98% 時間花在 GC 且可用堆 < 2%,拋出此異常。非洩漏場景下調整 -Xms -Xmx 可緩解。
-Xms:初始堆大小-Xmx:最大堆大小-Xms2g -Xmx2gulimit -v、swap 空間。java -Xmx2048m -version # 能輸出版本號說明支援,報錯則超過上限
| 特性 | PermGen (JDK 7及之前) | Metaspace (JDK 8+) |
|---|---|---|
| 存儲位置 | 堆內連續空間 | 本機內存(Native Memory) |
| 默認大小 | 固定較小(~82MB) | 無上限(受限於本機內存) |
| GC 行為 | 運行期極少清理 | 類卸載觸發條件嚴格,但可回收 |
| 調優參數 | -XX:PermSize -XX:MaxPermSize | -XX:MetaspaceSize -XX:MaxMetaspaceSize |
# JDK 7-
-XX:PermSize=128m -XX:MaxPermSize=256m
# JDK 8+
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
關鍵提醒:java -Xmx 測試的最大值實為 -Xmx + -XX:MaxPermSize/MaxMetaspaceSize 之和。若系統支援 1.5G 總計,則 -Xmx1g -XX:MaxMetaspaceSize=768m 會啟動失敗。
分配給 JVM 的堆越大,反而越容易發生此錯誤。
-Xmx + MaxPermSize + Code Cache + 直接內存 + JNI 內存(MaxProcessMemory - JVMMemory - ReservedOsMemory) / ThreadStackSize = MaxThreads
| JVM 堆配置 | 理論最大線程數 |
|---|---|
| -Xmx1.5g | ~380 |
| -Xmx1.0g | ~880 |
-Xmx 騰出本機內存。-Xss256k 或 -Xss512k(需測試避免 StackOverflowError)。boot.ini 加 /3GB /PAE 使用戶態達 3GB(需應用支援 Large Address Aware)。heap dump,定位 GC Root 引用鏈-Xms -Xmx 同值,-XX:MaxMetaspaceSize 設上限,-Xss 按併發需求調整-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=20m (JDK 9+) 或舊版 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc.log-Xms3g -Xmx3g \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps/ \
-XX:+UseContainerSupport -XX:InitialRAMPercentage=75.0 -XX:MaxRAMPercentage=75.0 \
-Xss512k \
-Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai