Java 運行時數據區域(一):名詞概念
- 1.線程私有的區域(Thread-Private)
- 1.1 程序計數器(Program Counter Register)
- 1.2 Java 虛擬機棧(Java Virtual Machine Stack)
- 1.3 本地方法棧(Native Method Stack)
- 2.線程共享的區域(Thread-Shared)
- 2.1 Java 堆(Java Heap)
- 2.2 方法區(Method Area)
- 3.直接內存(Direct Memory)
- 4.總結
Java 運行時數據區域(Runtime Data Areas)是 Java 虛擬機(JVM)在執行 Java 程序過程中會使用到的內存區域。
根據《Java虛擬機規範》的規定,這些區域可以分為以下幾個部分,其中一些是 所有線程共享的,另一些是 線程私有的。
1.線程私有的區域(Thread-Private)
這些區域的生命週期與線程相同,隨線程的創建而創建,隨線程的結束而銷燬。
1.1 程序計數器(Program Counter Register)
- 作用:可以看作是當前線程所執行的 字節碼的行號指示器。字節碼解釋器通過改變這個計數器的值來選取下一條需要執行的字節碼指令。分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
- 特性:
- 如果線程正在執行的是一個 Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址。
- 如果正在執行的是 Native 方法(本地方法,如 C/C++ 代碼),這個計數器的值則為 空(
Undefined)。 - 此區域是唯一一個在《Java虛擬機規範》中沒有規定任何
OutOfMemoryError情況的區域。
1.2 Java 虛擬機棧(Java Virtual Machine Stack)
- 作用:描述 Java 方法執行的內存模型。每個方法在執行的同時都會創建一個 棧幀(
Stack Frame),用於存儲 局部變量表、操作數棧、動態鏈接、方法出口 - 局部變量表:存放了編譯期可知的各種基本數據類型(
boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同於對象本身,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)和returnAddress類型(指向了一條字節碼指令的地址)。 - 異常狀況:
- 如果線程請求的棧深度大於虛擬機所允許的深度,將拋出
StackOverflowError異常。 - 如果虛擬機棧可以動態擴展(當前大部分 Java 虛擬機都可動態擴展,只不過規範中也允許固定長度的虛擬機棧),如果擴展時無法申請到足夠內存,就會拋出
OutOfMemoryError異常。
1.3 本地方法棧(Native Method Stack)
- 作用:與虛擬機棧非常相似。其區別在於:虛擬機棧為虛擬機執行 Java 方法(也就是字節碼)服務,而本地方法棧則為虛擬機使用到的 Native 方法服務。
- 異常狀況:與虛擬機棧一樣,也會拋出
StackOverflowError和OutOfMemoryError。
2.線程共享的區域(Thread-Shared)
這些區域是所有線程共享的,生命週期與虛擬機相同。
2.1 Java 堆(Java Heap)
- 作用:這是 Java 虛擬機中最大的一塊內存。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例以及數組都在這裏分配內存。
- GC 的主要區域:由於現代垃圾收集器基本都採用分代收集算法,所以 Java 堆中還可以細分為:新生代(
Young Generation)和 老年代(Old Generation/Tenured Generation)。新生代中更細緻地分為 Eden 空間、From Survivor 空間、To Survivor 空間等。 - 異常狀況:如果在堆中沒有內存完成實例分配,並且堆也無法再擴展時,將會拋出
OutOfMemoryError異常。
2.2 方法區(Method Area)
- 作用:用於存儲已被虛擬機加載的 類型信息、常量、靜態變量、即時編譯器編譯後的代碼緩存
- 運行時常量池(
Runtime Constant Pool):是方法區的一部分。Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是 常量池表(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後存放到方法區的運行時常量池中。 - HotSpot 虛擬機的實現:在 JDK 8 之前,方法區通常被稱為 永久代(
Permanent Generation)。從 JDK 8 開始,HotSpot 虛擬機完全移除了永久代,改用名為 元空間(Metaspace)的實現來代替,將其放在本地內存(Native Memory)中,而不是虛擬機內存中。 - 異常狀況:當方法區無法滿足內存分配需求時,將拋出
OutOfMemoryError。
3.直接內存(Direct Memory)
直接內存(Direct Memory)並不是虛擬機運行時數據區的一部分,也不是《Java虛擬機規範》中定義的內存區域。但它也被頻繁地使用,而且也可能導致 OutOfMemoryError 異常出現。
- 來源:在 JDK 1.4 中新加入了
NIO(New Input/Output)類,引入了一種基於 通道(Channel)與 緩衝區(Buffer)的 I/O 方式,它可以使用 Native 函數庫直接分配堆外內存,然後通過一個存儲在 Java 堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因為它避免了在 Java 堆和 Native 堆中來回複製數據。 - 大小限制:直接內存的分配不會受到 Java 堆大小的限制,但會受到本機總內存(包括 RAM 和 SWAP 區或者分頁文件)大小以及處理器尋址空間的限制。動態擴展時也可能出現
OutOfMemoryError。
4.總結
|
區域名稱
|
線程共享/私有
|
作用
|
異常
|
|
程序計數器 |
私有
|
當前線程執行的字節碼行號指示器
|
無
|
|
Java 虛擬機棧 |
私有
|
存儲 Java 方法的棧幀(局部變量、操作數棧等)
|
|
|
本地方法棧 |
私有
|
為 Native 方法服務
|
|
|
Java 堆 |
共享
|
存放對象實例和數組,GC 主戰場 |
|
|
方法區 |
共享
|
存儲類信息、常量、靜態變量、JIT 代碼等
|
|
|
直接內存 |
(不屬於運行時數據區)
|
NIO 使用的堆外內存,提高 I/O 性能
|
|