首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java OutOfMemoryError 完整解析:原因、類型與多環境解決方案

     編輯:Java 相關知識

前言

java.lang.OutOfMemoryError 是 Java 開發中最讓人頭痛的錯誤之一,字面意思是「記憶體不足」。當 JVM 無法分配足夠的記憶體給新建對象,且垃圾收集器也無法釋放更多空間時,就會拋出此錯誤。本文將系統梳理 OOM 的常見原因、各類錯誤類型,並針對 Tomcat、WebLogic、Resin、Eclipse+JBoss 等主流環境提供具體調優方案。

一、OutOfMemoryError 核心成因

導致 OOM 的根本原因通常集中在以下五類:

  1. 單次載入數據過大:例如一次性從資料庫查詢幾十萬筆記錄到 List 中,超出堆內存上限。
  2. 集合類引用未清理:List、Map 等集合持有對象引用,使用完畢未調用 clear() 或設為 null,導致 GC 無法回收。
  3. 死循環或大量重複創建對象:代碼邏輯缺陷導致無限創建對象實例,迅速耗盡堆空間。
  4. 第三方庫 Bug:引用的框架或庫存在內存洩漏缺陷。
  5. 啟動參數記憶體設定過小:-Xms、-Xmx、-XX:MaxPermSize(JDK 7 及以前)或 -XX:MaxMetaspaceSize(JDK 8+)配置不足。

二、常見錯誤類型與含義

錯誤訊息所屬區域典型場景
Java heap space堆內存對象實例過多、大對象分配、記憶體洩漏
PermGen space永久代類加載過多、JSP 預編譯、大量第三方 JAR(JDK 7 及以前)
Metaspace元空間類元數據過多、動態生成大量類(JDK 8+)
GC overhead limit exceededGC 佔用過高98% 時間花在 GC,且回收不到 2% 堆空間
Unable to create new native thread本地線程線程數過多、棧大小設定過大

三、通用排查與代碼層面優化

1. 代碼層面核心檢查點

  • 死遞歸/死循環:檢查遞歸終止條件、循環邏輯。
  • 大循環內 new 對象:將對象創建移出循環,或使用對象池/複用模式。
  • 一次性全量查詢:必須改為分頁查詢。上線前測試數據量少不易暴露,生產環境數據增長後極易觸發 OOM。建議每次查詢控制在 1,000~5,000 筆以內。
  • 集合對象未釋放:用完的 List、Map、Set 及時 clear() 或賦值 null,特別是靜態集合、單例中持有的集合。

2. 實戰案例回顧

  • Hibernate 全量查詢:改為 setFirstResult/setMaxResults 分頁,問題解決。
  • 壓測時 Session 未釋放:調用 session.invalidate() 主動銷毀。
  • 代碼死循環:修正邏輯判斷條件。
  • Tomcat 記憶體不足:調大 -Xms/-Xmx 參數。

四、主流容器/環境專項配置指南

1. Tomcat:Heap Space (堆內存不足)

原理:JVM 堆 = Young Generation + Old Generation。默認 -Xms = 物理內存 1/64,-Xmx = 1/4。當 98% CPU 用於 GC 且可用堆 < 2% 時拋出異常。

最佳實踐:

  • -Xms 與 -Xmx 設為相同值,避免動態擴縮容開銷。
  • -Xmx 不超過物理內存 80%。
  • -Xmn (新生代) 設為 -Xmx 的 1/4~1/3。

配置示例 (修改 TOMCAT_HOME/bin/catalina.sh,在 echo "Using CATALINA_BASE..." 下方添加):

JAVA_OPTS="-server -Xms2g -Xmx2g -Xmn512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

註:JDK 8+ 已移除永久代,改用 Metaspace,請用 MetaspaceSize/MaxMetaspaceSize 替代 PermSize/MaxPermSize。

2. Tomcat:PermGen Space / Metaspace (永久代/元空間不足)

成因:類、元數據存放區。JDK 7 為 PermGen,JDK 8+ 為 Metaspace(使用本地內存)。大量 JSP 預編譯、動態代理生成類、大量第三方 JAR 會耗盡此區。

配置示例 (同修改 catalina.sh):

# JDK 7
JAVA_OPTS="-server -XX:PermSize=128m -XX:MaxPermSize=256m"

# JDK 8+
JAVA_OPTS="-server -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"

建議:將公共第三方 JAR 移至 tomcat/shared/lib 或 common/lib,減少重複加載佔用。

3. WebLogic

修改 bea/weblogic/common/bin/commEnv.sh (Linux) 或 commEnv.cmd (Windows) 中的 MEM_ARGS:

MEM_ARGS="-Xms512m -Xmx1024m -XX:MaxPermSize=256m"  # JDK 7
MEM_ARGS="-Xms512m -Xmx1024m -XX:MaxMetaspaceSize=256m"  # JDK 8+

4. Eclipse + JBoss (開發環境)

  1. 點擊 Debug 圖標旁小箭頭 → Debug Configurations...
  2. 左側選 Generic Server → JBoss v4.2 at localhost。
  3. 切換 Arguments 標籤 → VM arguments 添加:
-Dprogram.name=run.bat -Djava.endorsed.dirs="D:/JBoss405/bin/../lib/endorsed" 
-Xms256m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m  # JDK 7
# 或 JDK 8+
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m

若用命令行啟動,修改 bin/run.conf 中的 JAVA_OPTS 同樣追加上述參數。

5. Resin

修改 /usr/local/resin/bin/httpd.sh 中的 args 參數:

args="-Xms256m -Xmx512m"

根據服務器實際物理內存由運維調整,原則同 Tomcat。

五、進階排查工具與建議

  • Heap Dump 分析:啟動參數加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump,事後用 Eclipse MAT、JVisualVM、Arthas 分析支配樹、GC Roots 引用鏈,精準定位洩漏對象。
  • GC 日誌:-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags (JDK 9+) 或 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/logs/gc.log (JDK 8),觀察 Full GC 頻率、耗時、回收效率。
  • 生產環境原則:先調參數保業務可用,再分析 Dump 治本。參數調大只是緩解,代碼層面洩漏必須修復。

六、總結

OutOfMemoryError 不是單一問題,而是「記憶體需求 > 可用記憶體」的綜合癥狀。解決路徑遵循:代碼優化(分頁、釋放引用、避免大對象) > 參數調優(-Xms/-Xmx/-Xmn/Metaspace) > 容器專項配置 > Dump 深度分析。養成壓測時監控內存、GC 曲線的習慣,才能在上線前攔截風險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved