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

Java 記憶體模型深度解析:堆、棧、常量池、方法區與物件分配實戰

     編輯:Java 相關知識

Java 記憶體模型(JMM)是理解物件生命週期、效能調優、避免 OOM 的基石。本文系統梳理堆、虛擬機棧、常量池、方法區、本地方法棧、程式計數器六大運行時資料區,並透過代碼實戰演示物件分配與引用關係。

一、JVM 運行時資料區總覽

區域線程私有/共享核心內容異常
程式計數器私有當前執行位元碼指令地址無
虛擬機棧私有棧幀(局部變數表、操作數棧、動態連結、返回地址)StackOverflowError / OOM
本地方法棧私有Native 方法調用StackOverflowError / OOM
堆共享幾乎所有物件實例、陣列OutOfMemoryError
方法區共享類別元資料、常量、靜態變數、JIT 編譯碼OutOfMemoryError
運行時常量池共享(屬方法區)編譯期常量、符號引用、字串常量池OOM
JDK 8 移除永久代,類別元資料移至元空間;字串常量池、靜態變數移至堆中。

二、核心區域深度解析

1. 虛擬機棧:方法執行的基本單位

  • 每個方法調用對應一個棧幀入棧/出棧。
  • 局部變數表:基本型別、物件引用、returnAddress。長度在編譯期確定。
  • 操作數棧:位元碼指令操作數暫存區。
  • 棧深度過大 → StackOverflowError;無法申請記憶體 → OOM。

2. 堆:物件分配主戰場

  • 新生代:Eden + 2 Survivor,Minor GC 高頻。
  • 老年代:大物件、長期存活物件,Major GC/Full GC 低頻。
  • TLAB(執行緒本地分配緩衝)優化多執行緒分配效能。

3. 常量池:編譯期與執行期的橋樑

  • Class 常量池:.class 檔案中,存字面量、符號引用。
  • 運行時常量池:類別載入後進入方法區,具備動態性(String.intern() 可擴充)。
  • 字串常量池:JDK 7+ 移至堆中,存放字串字面量及 intern() 字串。

三、代碼實戰:物件分配與引用關係圖解

案例代碼


class BirthDate {
    private int day, month, year;
    public BirthDate(int d, int m, int y) { day = d; month = m; year = y; }
}

public class Test {
    public static void main(String[] args) {
        int date = 9;                    // 局部基本型別 → 棧
        Test test = new Test();          // test引用→棧,物件→堆
        test.change(date);               // 參數i→棧,基本型別傳值
        BirthDate d1 = new BirthDate(7,7,1970); // d1引用→棧,物件→堆
    }
    public void change(int i) { i = 1234; } // i為局部變數,棧中,方法結束銷毀
}

記憶體狀態演進

  1. int date = 9:棧 分配局部變數 date,值 9。
  2. Test test = new Test():棧 分配引用 test;堆 分配 Test 物件。
  3. test.change(date):棧 新建棧幀,參數 i 接收值 9(值傳遞)。修改 i 不影響 date。方法結束,棧幀彈出,i 消失。
  4. BirthDate d1 = new BirthDate(7,7,1970):棧 分配引用 d1;堆 分配 BirthDate 物件(含欄位 day/month/year)。建構子參數 d,m,y 在棧中,建構子結束銷毀。
  5. main 結束:棧 中 date、test、d1 引用銷毀;堆 中 Test、BirthDate 物件等待 GC 回收。

四、字串與基本型別常量的特殊規則

字串:字面量 vs new


String s1 = "china";  // 常量池
String s2 = "china";  // 常量池,同一引用
String s3 = "china";  // 常量池,同一引用
// s1 == s2 == s3 → true

String ss1 = new String("china"); // 堆新物件
String ss2 = new String("china"); // 堆另一物件
// ss1 == ss2 → false;ss1.equals(ss2) → true

基本型別包裝類別緩存(-128~127)


Integer a = 100, b = 100;  // 緩存池,a == b → true
Integer c = 200, d = 200;  // 新物件,c == d → false
// 統一用 equals() 比較值

五、成員變數 vs 局部變數記憶體差異

總結:記憶體模型心智圖

棧 (Thread Stack)                    堆 (Heap)
├── main() 棧幀                      ├── Test 物件
│   ├── date=9 (int)                 │   └── (無實例欄位)
│   ├── test ────────────────────────┼──► (物件頭 + 實例資料)
│   └── d1 ──────────────────────────┼──► BirthDate 物件
│       change() 棧幀                │       ├── day=7
│       └── i=9 (參數)               │       ├── month=7
│                                    │       └── year=1970
│                                    │
常量池 (String Pool)                 │
├── "china" ◄── s1, s2, s3            │
└── (其他字面量)                      │

掌握這張圖,您就擁有了診斷記憶體洩漏、優化物件分配、解讀 GC 日誌的核心視角。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度成員變數(欄位)局部變數
位置堆中物件內部棧幀局部變數表
生命週期隨物件創建/銷毀隨方法進入/退出
預設值有(0/null/false)無(必須顯式初始化)
執行緒安全非安全(需同步)天然安全(線程私有)