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

《深入理解 Java 虛擬機》學習筆記:第二章 Java 內存區域與 OutOfMemoryError 分析

     編輯:Java 綜合教學

JVM 運行時數據區域(JDK 8 模型)

Java 虛擬機在執行程序時將管理的內存劃分為若干個數據區域,每個區域有特定用途、創建/銷毀時間。線程私有區域隨線程啟動/結束,線程共享區域隨虛擬機啟動/關閉。

區域線程性核心功能OOM 風險參數控制
程序計數器私有當前線程執行字節碼行號指示器、分支/循環/跳轉基礎無(規定唯一不 OOM 區域)無
虛擬機棧私有Java 方法執行內存模型:棧幀(局部變量表/操作數棧/動態連接/返回地址)StackOverflowError (深度遞歸)
OutOfMemoryError (擴展失敗)
-Xss (棧大小)
本地方法棧私有Native 方法服務同虛擬機棧通常合併配置
Java 堆共享對象實例、數組分配區域,GC 主戰場高發:java.lang.OutOfMemoryError: Java heap space-Xms -Xmx
方法區共享類信息、常量、靜態變量、即時編譯代碼緩存OutOfMemoryError: Metaspace (JDK 8+)-XX:MetaspaceSize -XX:MaxMetaspaceSize
運行時常量池共享方法區一部分:字面量、符號引用、動態添加屬方法區屬方法區
直接內存共享NIO DirectBuffer 分配的堆外內存OutOfMemoryError: Direct buffer memory-XX:MaxDirectMemorySize

重點:HotSpot 對象訪問定位方式

reference(棧上引用)→ 堆中對象實例數據 + 方法區類型數據。兩種主流方式:

1. 句柄訪問

棧 reference → 堆句柄池 → [實例數據指針, 類型數據指針] → 實際對象 / 方法區類信息

句柄訪問示意圖

  • 優點:reference 存儲穩定句柄地址,對象移動(GC 壓縮/複製)時只修改句柄池指針,reference 本身不變
  • 缺點:雙重間接訪問,性能損耗

2. 直接指針訪問(HotSpot 默認)

棧 reference → 直接指向堆中對象實例數據 → 對象頭包含類型指針 → 方法區類信息

直接指針訪問示意圖

  • 優點:節省一次指針定位開銷,訪問速度更快(對象訪問極高頻)
  • 缺點:對象移動時需更新所有 reference(HotSpot 用佈局鎖/卡表等技術解決)

對象創建內存佈局(HotSpot)

[對象頭 Header] [實例數據 Instance Data] [對齊填充 Padding]
  • 對象頭:
    • Mark Word:哈希碼、GC 分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程 ID、偏向時間戳
    • Klass Pointer:指向方法區類元數據的指針(決定對象類型)
    • 數組長度:若為數組對象
  • 實例數據:字段內容(按分配策略排序:長類型→短類型、父類→子類)
  • 對齊填充:8 字節對齊要求

常見 OOM 場景與排查

異常信息原因排查工具/命令解決方向
Java heap space堆內存不足:對象過多/大、洩漏、堆太小jmap -histo:live pid
MAT 分析 heap dump
JVisualVM
優化代碼/集合大小、檢查洩漏、增大 -Xmx
Metaspace類元數據區不足:動態生成大量類、類加載器洩漏jcmd pid VM.metaspace
jstat -gcmetacapacity pid
增大 MaxMetaspaceSize、檢查動態代理/CGLIB/ScriptEngine 洩漏
StackOverflowError棧幀過深:遞歸無終止、循環調用jstack pid 分析棧軌跡改遞歸為迭代、增加 -Xss、修正邏輯
Direct buffer memory堆外內存不足:NIO 大量 DirectBuffer 未釋放jcmd pid VM.native_memory summary顯式調用 Cleaner.clean()、增大 MaxDirectMemorySize、用堆內緩衝
GC overhead limit exceededGC 佔用 >98% CPU 但回收 <2% 堆同 heap space同 heap space、或關閉 -XX:-UseGCOverheadLimit(治標)

JDK 版本演進關鍵點

  • JDK 6:永久代,字符串常量池在永久代
  • JDK 7:字符串常量池、靜態變量移至堆
  • JDK 8:移除永久代,改用元空間(本地內存),類元數據存儲於此
  • JDK 11+:ZGC、Shenandoah 低延遲 GC,壓縮類指針

實戰排查命令速查

# 1. 查看進程
jps -l

# 2. 堆內存快照
jmap -dump:live,format=b,file=heap.hprof 
# 分析用 MAT (Memory Analyzer Tool) 打開 .hprof

# 3. 實時統計
jstat -gcutil  1000 10  # 每秒輸出一次,共10次

# 4. 線程快照
jstack  > thread_dump.txt
# 分析死鎖、CPU 高佔用線程

# 5. 類加載統計
jstat -class 

# 6. 原生內存追蹤 (需啟動參數 -XX:NativeMemoryTracking=detail)
jcmd  VM.native_memory summary

核心心法:理解內存區域劃分是排查 OOM 基石。堆是對象之家,棧是執行之基,方法區是類之倉,直接內存是 NIO 之野。OOM 本質是「申請內存失敗」,定位是哪個區域、哪類對象、哪行代碼、為什麼不釋放。工具鏈:jstat 監控 → jmap 導出 → MAT 分析 → 代碼定位 → 優化驗證。

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