JVM內存模型及分區詳解
JVM內存模型主要分為以下幾個核心區域,每個區域有特定的用途和存儲內容:
1. 程序計數器(Program Counter Register)
存儲內容:
- 當前線程執行的字節碼行號指示器
- 對於Java方法,記錄正在執行的虛擬機字節碼指令的地址
- 對於Native方法,值為undefined(未定義)
特點:
- 線程私有,每個線程都有獨立的程序計數器
- 內存空間最小
- 唯一不會發生OutOfMemoryError的區域
2. Java虛擬機棧(Java Virtual Machine Stack)
存儲內容:
- 方法執行的棧幀(Stack Frame)
-
每個棧幀包含:
- 局部變量表:存放方法參數和局部變量(基本數據類型和對象引用)
- 操作數棧:方法執行過程中的臨時數據操作區域
- 動態鏈接:指向運行時常量池的方法引用
- 方法出口:方法返回地址或異常處理信息
特點:
- 線程私有,生命週期與線程相同
- 棧深度過大可能導致StackOverflowError
- 內存不足時可能導致OutOfMemoryError
3. 本地方法棧(Native Method Stack)
存儲內容:
- 為Native方法(非Java實現的方法)提供服務的棧空間
- 存儲Native方法調用的相關信息
特點:
- 線程私有
- 可能導致StackOverflowError或OutOfMemoryError
- 具體實現依賴於JVM廠商
4. Java堆(Java Heap)
存儲內容:
- 所有對象實例("new"出來的對象)
- 對象數組
- 運行時常量池中的部分內容(JDK 7及以後,部分常量池移至堆中)
特點:
- 線程共享,是JVM中最大的內存區域
- 垃圾收集器主要管理的區域(GC堆)
-
可分為新生代和老年代:
-
新生代:
- Eden區:新對象優先分配的區域
- Survivor 0區(S0/From):經過第一次GC後存活的對象
- Survivor 1區(S1/To):GC過程中的目標區域
- 老年代:存放經歷多次GC仍存活的對象
-
- 內存不足時拋出OutOfMemoryError
5. 方法區(Method Area)/ 元空間(Metaspace)
存儲內容:
- 類元數據信息:類的結構信息、字段信息、方法信息
- 運行時常量池:編譯期生成的各種字面量和符號引用
- 靜態變量(類變量)
- 即時編譯器編譯後的代碼緩存(JIT編譯後的代碼)
特點:
- 線程共享
-
JDK 7及以前稱為永久代(PermGen),JDK 8及以後改為元空間(Metaspace)
- 元空間使用本地內存而非JVM堆內存
- 內存不足時拋出OutOfMemoryError
6. 運行時常量池(Runtime Constant Pool)
存儲內容:
- 字面量:字符串字面量、基本類型常量值
- 符號引用:類和接口的全限定名、字段名稱和描述符、方法名稱和描述符
- 直接引用:直接指向目標的指針、相對偏移量或句柄
特點:
- 是方法區的一部分
- 具備動態性,運行期間也可以將新的常量放入池中(如String.intern()方法)
7. 直接內存(Direct Memory)
存儲內容:
- 通過NIO的ByteBuffer.allocateDirect()分配的內存
- 不屬於JVM運行時數據區的一部分,但被頻繁使用
特點:
- 使用本地內存
- 訪問速度通常快於Java堆內存
- 可能導致OutOfMemoryError(當本地內存不足時)
各區域內存分配的典型流程
- 類加載時,類元數據存入方法區/元空間
- 方法調用時,在虛擬機棧創建棧幀
- 創建對象時,優先在Eden區分配內存
- 對象經過多次GC後,可能進入老年代
- 垃圾收集器負責回收堆中不再引用的對象內存