執行時資料區主要包括:方法區,堆,Java 虛擬機器棧,程式計數器,本地方法棧。
其中方法區和堆所有執行緒共享,Java棧,程式計數器,本地方法棧執行緒私有。
程式計數器
一塊較小的記憶體空間,可以看做是當前執行緒所執行的位元組碼行號的指示器;
位元組碼直譯器工作時,通過改變計數器的值 選取下一條執行的位元組碼指令;(一些基本功能都需要依賴計數器來完成如:分支、迴圈、跳轉、異常處理、執行緒恢復等)
Java 虛擬機器多執行緒是通過執行緒間輪流切換來分配給處理器執行時間;在確定時間節點,一個處理器(一核)只會執行一個執行緒的指令。
為保證執行緒切換回來後能恢復到原執行位置,各個執行緒間計數器互相不影響,獨立儲存(稱之為執行緒私有的記憶體)。
當執行緒正執行 Java 程式時:程式計數器 記錄正在執行的虛擬機器位元組指令地址。
執行 native
方法,計數器值為空 undefined
;
該記憶體區域是唯一一個 Java 虛擬機器規範中沒有規定任何 OutOfMemoryError
情況的記憶體區域;
這塊記憶體區域是執行緒私有的。
虛擬機器棧
虛擬機器棧是方法的工作空間,由一個一個的棧幀組成,棧幀是在每一個方法呼叫時產生的。
每一個棧幀由區域性變數區
、運算元棧
等組成。每建立一個棧幀壓棧,當一個方法執行完畢之後則出棧。
- 如果出現方法遞迴呼叫出現死迴圈的話就會造成棧幀過多,最終會丟擲
StackOverflowError
。- 若執行緒執行過程中棧幀大小超出虛擬機器棧限制,則會丟擲
StackOverFlowError
。- 若虛擬機器棧允許動態擴充套件,但在嘗試擴充套件時記憶體不足,或者在為一個新執行緒初始化新的虛擬機器棧時申請不到足夠的記憶體,則會丟擲
OutOfMemoryError
。
這塊記憶體區域也是執行緒私有的。
Java 堆
Java 堆是整個虛擬機器所管理的最大記憶體區域,所有的物件建立都是在這個區域進行記憶體分配。
堆區包括屬性空間和方法空間,屬性的型別決定開闢空間大小,屬性個數決定開闢空間數量,堆中存放方法引用的空間大小為4個位元組
這塊區域也是垃圾回收器重點管理的區域,由於大多數垃圾回收器都採用分代回收演算法
,所有堆記憶體也分為 新生代(Young)
、老年代(Old)
,可以方便垃圾的準確回收。
新生代 ( Young ) 又被劃分為三個區域:Eden、From Survivor、To Survivor。
新生代:Young Generation
,主要用來存放新生的物件。
老年代:Old Generation
或者稱作 Tenured Generation
,主要存放應用程式宣告週期長的記憶體物件。
這塊記憶體屬於執行緒共享區域。
方法區
在 Sun JDK 中這塊區域對應的為 PermanetGeneration ,又稱為持久代,方法區是堆的邏輯部分。
方法區主要用於存放已經被虛擬機器載入的類資訊,如常量
,靜態變數
。
這塊區域也被稱為永久代
。
由於持久代內可能會發生記憶體洩露或溢位等問題而導致的 java.lang.OutOfMemoryError: PermGen
,JEP小組從 JDK1.7
開始就籌劃移除持久代(JEP 122: Remove the Permanent Generation),並且在 JDK 1.7
中把字串常量,符號引用等移出了持久代。到了 Java 8
,持久代被徹底地移出了 JVM
,取而代之的是元空間(Metaspace
):
In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace.
所以從Java 8開始,方法區被移至 Metaspace 內。
這塊記憶體屬於執行緒共享區域。
執行時常量池
執行時常量池是class檔案中每一個類或介面的常量池表的執行時表示形式,是方法區的一部分。
它包括了若干種不同的常量。常量池表存放編譯器生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中存放。執行時常量池具有動態性,執行期間也可以將新的量放到執行時常量池中,典型的應用是String類的intern方法:
public native String intern()
複製程式碼
JDK 1.7
開始,字串常量和符號引用等就被移出持久代
:
- 符號引用遷移至系統堆記憶體(
Native Heap
) - 字串字面量遷移至Java堆(
Java Heap
)