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

Java OutOfMemoryError 兩大類型深度解析與解決方案

     編輯:Java 程式設計入門

JVM 記憶體區域速覽

在排查 OOM 之前,必須先建立 JVM 記憶體模型的心智圖:

  • Heap(堆):物件實例分配區,GC 主戰場,分代為 Young/Old
  • Metaspace / PermGen(永久代/元空間):類元資料、常數池、方法表
  • Java Stack(虛擬機棧):每執行緒私有,存基本型別、物件引用、方法幀
  • Native Method Stack / Direct Memory:JNI、NIO 直接記憶體

💡 Java 8 移除 PermGen,改用 Metaspace(Native Memory),參數從 -XX:MaxPermSize 改為 -XX:MaxMetaspaceSize

情況一:java.lang.OutOfMemoryError: Metaspace / PermGen space

成因

  • 載入過多類(大量 JAR、動態代理、JSP 編譯、OSGi 熱部署)
  • 類別載入器洩漏(重新部署未卸舊類、ThreadLocal 未清理)
  • 字串常數池過大(String.intern() 濫用)

解決方案

  1. 調大上限(治標)
    # Java 8+
    -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
    
    # Java 7/6
    -XX:PermSize=64m -XX:MaxPermSize=128m
  2. 類卸載優化(治本)
    啟用 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC(CMS/G1 預設開啟)
  3. 排查洩漏
    jcmd <pid> GC.class_histogram 統計類實例數,配合 MAT/Eclipse Memory Analyzer 找 GC Root 鏈路
  4. 共享庫優化
    Tomcat 多應用共用 JAR 放入 $CATALINA_HOME/lib,減少重複載入

情況二:java.lang.OutOfMemoryError: Java heap space

成因

  • 物件創建速度 > GC 回收速度(大集合、快取無上限、記憶體洩漏)
  • 單次請求分配巨大物件(巨量報表、大檔案一次性讀入)
  • Heap 設定過小(容器限制、參數遺漏)

解決方案

  1. 程式碼層面(根治)
    • 流式處理替代全量載入(BufferedReader、Stream API、POI SAX 模式)
    • 快取加入淘汰策略(Caffeine、Guava Cache、Redis TTL)
    • 檢查靜態集合、ThreadLocal、監聽器未移除等洩漏點
  2. JVM 參數調優(緩解)
    -Xms2g -Xmx4g -XX:NewRatio=2 -XX:SurvivorRatio=8
    • -Xms=-Xmx 避免動態擴縮容震盪
    • NewRatio 調整新生代/老年代比例,視物件存活特性調優
  3. GC 演算法選型
    • < 4GB:G1(-XX:+UseG1GC)平衡延遲/吞吐
    • > 4GB 低延遲:ZGC(JDK 11+)或 Shenandoah
    • 批次處理/高吞吐:Parallel GC(-XX:+UseParallelGC)

實戰排查流程圖

OOM 發生
   │
   ├─ 確認錯誤類型(Metaspace vs Heap)
   │
   ├─ 產生 Heap Dump(-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path)
   │
   ├─ MAT 開啟 → Histogram → 按 Retained Size 排序
   │
   ├─ 定位佔用最大的物件 → Path to GC Roots → 找強引用鏈
   │
   └─ 修復代碼 / 調整參數 / 升級架構

最佳實踐清單

  • ✅ 生產環境務必開啟 Heap Dump 自動導出
  • ✅ 監控系統接入 JVM 指標(Heap/Metaspace/GC 次數/停頓)
  • ✅ 定期進行壓力測試,驗證記憶體增長曲線
  • ✅ 避免在迴圈中大量 new String()、BigDecimal、集合擴容
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved