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

深度解析 Java 記憶體模型:堆 vs 棧,物件生命週期全圖解

     編輯:Java 相關知識

核心結論先行

  • 棧:存放基本型別區域變數、物件參考變數 (引用)。執行緒私有,隨方法進出自動分配/釋放,速度極快。
  • 堆:存放 new 出來的物件實體、陣列。全執行緒共享,由 GC (垃圾回收器) 管理生命週期,分配較慢但靈活。
  • 參考變數 = 棧上的指標:指向堆中物件的起始位址。Java 無指標運算,但參考本質上是安全的指標。

圖解記憶體分配流程

方法執行:
  String s = new String("Hello");
  int len = s.length();

棧幀:
  [s: ref] ──────┐
  [len: 5]       │
                 ▼
堆:
  +------------------+
  | String Object    |
  | value: [H,e,l,l,o]|
  | hash: 12345      |
  +------------------+

詳細機制

1. 基本型別與參考在棧上

void foo() {
    int a = 10;        // 基本型別直接在棧分配 4 bytes
    Object obj = new Object(); // obj (參考) 在棧,new Object() 實體在堆
}

離開方法時,棧幀彈出,a、obj 參考銷毀。堆中物件若無其他參考指向,變為「垃圾」,等待 GC 回收。

2. 物件在堆上,參考在棧上

  • new 在堆分配記憶體,回傳位址給棧上參考變數。
  • 多個參考可指向同一堆物件 (共享)。
  • 參考變數超出作用域釋放,但堆物件不會立即釋放,需等 GC。

3. 為什麼 Java 較佔記憶體?

  • 物件頭 (Object Header):含 Mark Word (鎖、GC 年齡、Hash)、Class Pointer (指向類元資料)、陣列長度 (若是陣列)。典型 12-16 bytes (64-bit JVM 未開啟壓縮指標時)。
  • 記憶體對齊補齊 (8 bytes 倍數)。
  • GC 需額外空間演算法 (標記-清除/整理/分代)。

進階:方法區、常量池、直接記憶體

區域存放內容共享性備註
方法區 / Metaspace (JDK8+)類元資料、靜態變數、常量、即時編譯代碼全域共享JDK8 移出堆,使用本地記憶體
執行緒私有棧基本型區域變數、參考、幀資料執行緒私有-Xss 設定大小
堆物件實例、陣列全域共享-Xms/-Xmx 設定,GC 主戰場
直接記憶體NIO DirectBuffer、JNI 本地分配進程共享-XX:MaxDirectMemorySize 控制,不受堆限制但受限於物理記憶體

常見誤區澄清

  1. 「參數傳遞是傳引用」 ❌:Java 唯一傳值。傳物件時複製的是參考值 (位址),方法內改變參考指向不影響外部;但透過參考修改物件內部狀態則會影響外部。
  2. 「區域變數一定在棧」 ❌:逃逸分析優化下,未逃逸物件可能標量替換、棧上分配、同步消除 (JDK 7+ 預設開啟)。
  3. 「堆只能存物件」 ❌:陣列也是物件,在堆上;基本型別陣列元素直接在堆連續儲存。

調優建議

  • 減少物件創建:複用、物件池、基本型別集合 (Trove/FastUtil)。
  • 縮小作用域:讓參考盡早失效,助 GC。
  • 避免大物件/長生命週期物件頻繁分配:直接進老年代觸發 Full GC。
  • 監控 Metaspace / 直接記憶體 防 OOM。

總結

掌握「棧存參考/基本型、堆存物體、GC 管堆」這條主線,再理解物件頭結構、逃逸分析、分代收集,即可從根上解決記憶體洩漏、OOM、GC 停頓等疑難雜症。

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