線上系統拋出 java.lang.OutOfMemoryError: PermGen space,導致服務不可用。PermGen(Permanent Generation)是 JVM 存放類別元資料(Class、Method、Field 等)的區域,JDK 8 起已移除並由 Metaspace 取代,但許多舊系統仍運行於 JDK 6/7 環境。
String.intern() 存放於 PermGen,大量調用易耗盡空間。MaxPermSize=64m(32 位)或 82m(64 位),易不足。# 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 最大值。設定相同值可避免動態擴容開銷。
tools.jar)。在啟動腳本 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 連線。
不重啟應用、不修改代碼,動態掛載探針追蹤特定方法調用:
// 追蹤某 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 執行。
| 場景 | 解決策略 |
|---|---|
| 第三方 JAR 過多 | 調大 MaxPermSize;清理無用依賴;升級 JDK 8+ 使用 Metaspace(自動擴展、預設無上限)。 |
| JSP 大量預編譯 | 生產環境關閉預編譯或限制並發編譯數。 |
| 動態代理/字節碼生成 | 檢查 CGLIB/ASM/Javassist 使用是否重複生成相同類別;啟用快取機制。 |
| 熱部署/插件系統 | 確保自定義 ClassLoader 生命週期管理正確,卸載時斷開所有強引用(包括 ThreadLocal、緩存、監聽器)。 |
| String.intern() 濫用 | JDK 7+ 已移至 Heap;JDK 6 需避免大量 intern 或調大 PermGen。 |