運行時的數據區域
一.程序計數器
目的:作爲當前線程所執行字節碼的行號指示器
原理:通過字節碼解釋器改變計數器的值來選取下一條字節碼指令
特點:
1.佔用較小的內存空間
2.每條線程需要一個獨立的程序計數器
3.Native方法不需要程序計數器,因爲它不需要解釋器
4.不同線程的該內存區域相互獨立
方法類型 | 計數器狀態 |
---|---|
Java方法 | 記錄正在執行字節碼地址 |
Native方法 | 空 |
異常處理: 當沒有內存區域可擴展的時候,拋出OutOfMemoryError
二.Java虛擬機棧
目的:描述Java執行時的內存模型
原理:當一個JAVA方法執行的時候,會在虛擬機棧上建立一個棧幀,用來保存 局部變量表,操作數棧,動態鍵棧,方法出入的信息(eg:方法返回值)。
特點:
1.廣義上的棧就是指的是,虛擬機棧中的幾部變量表
2.進入一個方法時,局部變量空間是確定的,不會再改變
3.不同線程的該內存區域相互獨立
異常處理:
1.若線程請求的棧深度 > 虛擬機允許的深度,拋出StackOverflowError
2.若動態虛擬機棧可以動態擴展(絕大多數),若擴展時無法申請足夠的內存,拋出 OutOfMemoryError
局部變量表:
目的:使用 局部變量表 完成參數值到 參數變量列表 的傳遞過程
原理:是一個以字長爲單位的數組,用於存放方法參數 和 方法內定義的局部變量索引
索引類型包括:基本變量類型,對象引用類型, returnAdress類型
1.reference類型(對象引用類型),作爲對象引用指針,指明瞭對象在 JAVA堆 中的起始地址 和在 方法區 的對象類型數據
2. returnAdress類型,指向一條字節碼指令的地址
-
局部變量表中一個slot代表一個變量槽,長度和CPU架構的位長是相同的。
-
64位長度的long和double類型佔用2個局部變量表空間(slot),其他的均爲1個單位大小。
-
在第0位slot處存放該方法所屬對象實例的引用(在Java堆中),程序中用
this關鍵字
進行訪問 -
可複用:超出作用域的slot變量將被GC回收進行覆寫
操作數棧
目的:作爲JVM的工作區,對數據進行操作。
原理:同樣也是一個以字長爲單位的數組,用於存放操作數的值。大多數指令都要從這裏彈出數據,執行運算,然後把結果壓回操作數棧。
解析:
void Method(){
int a = 1;
int b = 2;
int result = a + b;
}
內部過程
1.在局部變量表分配三個slot,分別指向 a , b,result。其中 a,b有初值
2.將局部變量表中的a,b的值放入操作數棧。
3.在操作數棧中彈出a,b的值彈出進行運算,接着將結果壓入操作數棧中。
4.從操作數棧彈出結果,放到局部變量表中result相應的位置
三.本地方法棧
目的:描述Native方法執行時的內存模型
原理: 形如Java虛擬機棧
特點: 不同線程的該內存區域相互獨立
異常處理: 與Java虛擬機棧出現的異常是相同的
有些虛擬機會將本地方法棧與Java虛擬機棧合二爲一(eg:HotSpot虛擬機)
四.Java堆
目的: 存放幾乎所有對象的實例
原理: 將實例存放在一塊 物理不連續,但是邏輯上連續的內存空間上。
特點:
1.是JVM管理的內存中最大的一塊區域
2.所有線程共享一塊內存區域
異常處理: 通常實例分配的時候,堆是可擴展的,但是當無法再擴展的時候,會拋出OutOfMemoryError
五.方法區(非堆)
目的: 作爲Java堆的邏輯部分
原理: 存儲已被虛擬機加載的 類信息,常量,靜態變量,即時編譯器編譯後的代碼 等數據
特點:
1.物理不連續
2.所有線程共享一塊內存區域
3.可以選擇不實現GC
異常處理: 雖然方法區是可以擴展的,但是當無法滿足內存分配時,拋出OutOfMemoryError
運行時常量池
目的: 是方法區的一部分,用於存放編譯期間生成的各種字面量與符號
原理: 在類加載後,將“常量”存放在該區域
特點: 動態性:在運行期間也能將新的常量放入池中
eg:String類的intern方法(TODO )
JDK1.8後字符串常量池放到了堆中,不再是在方法區中,而是在Java堆中
直接內存
目的: 直接引用分配在Java堆外Native函數庫的內存,避免了Java堆與Native堆來回複製操作
原理: 通過一個存儲在Java堆中的DirectByteBuffer
對象,作爲這塊內存的引用進行操作
特點: 顯然直接內存分配不受Java堆大小限制,但受到本機物理的內存大小限制。
異常處理: 當佔用總和超出物理內存大小限制的時候,拋出OutOfMemoryError