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

Java PermGen Space 記憶體溢出原因分析與排查實戰

     編輯:Java 相關知識

問題現象

線上系統拋出 java.lang.OutOfMemoryError: PermGen space,導致服務不可用。PermGen(Permanent Generation)是 JVM 存放類別元資料(Class、Method、Field 等)的區域,JDK 8 起已移除並由 Metaspace 取代,但許多舊系統仍運行於 JDK 6/7 環境。

PermGen 溢出常見原因

  1. 類別載入過多:專案依賴大量第三方 JAR、動態產生類別(JSP 編譯、CGLIB/ASM 動態代理、JSP 預編譯)。
  2. 類別卸載不當:自定義 ClassLoader 持有類別引用未釋放,導致類別無法被 GC 回收(常見於熱部署、OSGi、插件系統)。
  3. 字串常量池過大:JDK 6/7 中 String.intern() 存放於 PermGen,大量調用易耗盡空間。
  4. 預設值過小:JDK 6/7 預設 MaxPermSize=64m(32 位)或 82m(64 位),易不足。

快速緩解:調大 PermGen 參數

# Linux (catalina.sh)
JAVA_OPTS="-Xms1024m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m"

# Windows (catalina.bat)
set JAVA_OPTS=-Xms1024m -Xmx1024m -XX:PermSize=256M -XX:MaxPermSize=256m

說明:PermSize 初始值、MaxPermSize 最大值。設定相同值可避免動態擴容開銷。

深度排查:視覺化工具定位洩漏

1. 本地監控:jvisualvm / jconsole

  • JDK 內建工具,支援本地進程連接與記憶體掃描。
  • 若無法偵測到 Tomcat 進程,檢查是否使用 JRE 而非 JDK 啟動(需 tools.jar)。

2. 遠程監控:JMX 連線

在啟動腳本 JAVA_OPTS 追加:

-Djava.rmi.server.hostname=192.168.1.101 \
-Dcom.sun.management.jmxremote.port=9000 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false

驗證埠口:netstat -an | grep 9000。使用 jvisualvm → File → Add JMX Connection 輸入 host:port 連線。

3. 關鍵觀察指標

  • Perm Gen 使用趨勢:持續上升不回落 → 類別洩漏。
  • 已載入類別數量:對比預期值,異常增長提示動態生成類別過多。
  • GC 後 Perm Gen 釋放量:Full GC 後未釋放 → 類別被強引用持有。

進階診斷:BTrace 動態追蹤

不重啟應用、不修改代碼,動態掛載探針追蹤特定方法調用:

// 追蹤某 ClassLoader 的 loadClass 呼叫堆疊
@BTrace public class ClassLoaderTrace {
    @OnMethod(clazz = "+java.lang.ClassLoader", method = "loadClass")
    public static void traceLoadClass(@ProbeClassName String pcn, @ProbeMethodName String pmn, String className) {
        println("ClassLoader.loadClass: " + className);
        jstack(); // 印出調用堆疊
    }
}

編譯後用 btrace -cp ClassLoaderTrace.class 執行。

根治方案總結

遷移建議

長遠之計:升級至 JDK 8+。Metaspace 使用本地記憶體,預設無大小限制(可用 -XX:MaxMetaspaceSize 限制),類別卸載機制更完善,徹底解決 PermGen 痛點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景解決策略
第三方 JAR 過多調大 MaxPermSize;清理無用依賴;升級 JDK 8+ 使用 Metaspace(自動擴展、預設無上限)。
JSP 大量預編譯生產環境關閉預編譯或限制並發編譯數。
動態代理/字節碼生成檢查 CGLIB/ASM/Javassist 使用是否重複生成相同類別;啟用快取機制。
熱部署/插件系統確保自定義 ClassLoader 生命週期管理正確,卸載時斷開所有強引用(包括 ThreadLocal、緩存、監聽器)。
String.intern() 濫用JDK 7+ 已移至 Heap;JDK 6 需避免大量 intern 或調大 PermGen。