Java 虛擬機定義了若干種程序運行期間會使用到的運行時數據區,其中有一些會隨著虛擬機
啟動而創建,隨著虛擬機退出而銷毀。另外一些則是與線程一一對應的,這些與線程對應的數據區
域會隨著線程開始和結束而創建和銷毀。
?
寄存器
? ? ? ?Java 虛擬機可以支持多條線程同時執行(可參考《 Java 語言規范》第 17 章),每一條 Java
虛擬機
線程都有自己
的 PC( Program Counter)寄存器。在任意時刻,一條 Java 虛擬機線程
只會執行一個方法的代碼,這個正在被線程執行的方法稱為該線程的當前方法( Current
Method, §2.6)。如果這個方法不是 native 的,那 PC 寄存器就保存 Java 虛擬機正在執行的
字節碼指令的地址,如果該方法是 native 的,那 PC 寄存器的值是 undefined。 PC 寄存器的容
量至少應當能保存一個 returnAddress 類型的數據或者一個與平臺相關的本地指針的值。
?
Java 虛擬機棧
? ? ? ?每一條 Java 虛擬機 線程 都有自己 私有 的 Java 虛擬機棧( Java Virtual Machine Stack)①,這個棧與線程同時創建,用于存儲棧幀(Frames)。 Java 虛擬機棧的作用與傳統語言(例如 C 語言)中的棧非常類似,就是用于存儲 局部變量 與一些 過程結果 的地方。另外,它在方法調用和返回中也扮演了很重要的角色。因為除了棧幀的出棧和入棧之外, Java 虛擬機棧不會再受其他因素的影響,所以棧幀可以在堆中分配②, Java 虛擬機棧所使用的內存不需要保證是連續的。
? ? ? ?
Java 虛擬機規范允許 Java 虛擬機棧被實現成固定大小的或者是根據計算動態擴展和收縮的。如果采用固定大小的 Java 虛擬機棧設計,那每一條線程的 Java 虛擬機棧容量應當在線程創建的時候獨立地選定。 Java 虛擬機實現應當提供給程序員或者最終用戶調節虛擬機棧初始容量的手段,對于可以動態擴展和收縮 Java 虛擬機棧來說,則應當提供調節其最大、最小容量的手段。
Java 虛擬機??赡馨l生如下異常情況:
*?
如果線程請求分配的棧容量超過 Java 虛擬機棧允許的最大容量時, Java 虛擬機將會拋出一
個 StackOverflowError 異常。
* 如果 Java 虛擬機棧可以動態擴展,并且擴展的動作已經嘗試過,但是目前無法申請到足夠的內存去完成擴展,或者在建立新的線程時沒有足夠的內存去創建對應的虛擬機棧,那 Java 虛擬機將會拋出一個 OutOfMemoryError 異常。
①?在?Java?虛擬機規范第一版之中,?Java?虛擬機棧也被稱為“?Java?棧”。
②?譯者注:請讀者注意避免混淆?Stack、?Heap?和?Java?(?VM)?Stack、?Java Heap?的概念,?Java?虛擬機
的實現本身是由其他語言編寫的應用程序,在?Java?語言程序的角度上看分配在?Java Stack?中的數據,而在實現虛擬機的程序角度上看則可以是分配在?Heap?之中。
?
Java
堆
在 Java 虛擬機中,堆( Heap)是可供各條線程共享的運行時內存區域,也是供所有類實例和數組對象分配內存的區域。Java 堆在虛擬機啟動的時候就被創建,它存儲了被自動內存管理系統( Automatic Storage
Management System,也即是常說的“ Garbage Collector(垃圾收集器)”)所管理的各種對象,這些受管理的對象無需,也無法顯式地被銷毀。本規范中所描述的 Java 虛擬機并未假設采用什么具體的技術去實現自動內存管理系統。虛擬機實現者可以根據系統的實際需要來選擇自動內存管理技術。 Java 堆的容量可以是固定大小的,也可以隨著程序執行的需求動態擴展,并在不需要過多空間時自動收縮。 Java 堆所使用的內存不需要保證是連續的。
Java 虛擬機實現應當提供給程序員或者最終用戶調節 Java 堆初始容量的手段,對于可以動
態擴展和收縮 Java 堆來說,則應當提供調節其最大、最小容量的手段。
Java 堆可能發生如下異常情況:
? 如果實際所需的堆超過了自動內存管理系統能提供的最大容量,那 Java 虛擬機將會拋出一個
OutOfMemoryError 異常。
?
方法區
在
Java
虛擬機中,方法區(
Method Area
) 是可供各條線程共享的運行時內存區域。方法
區與傳統語言中的編譯代碼儲存區(
Storage Area Of Compiled Code
)或者操作系統進程
的正文段(
Text Segment
)的作用非常類似,它存儲了每一個類的結構信息,例如運行時常量
池(
Runtime Constant Pool
)、字段和方法數據、構造函數和普通方法的字節碼內容、還包
括一些在類、實例、接口初始化時用到的特殊方法(
§
2.9
)。
方法區在虛擬機啟動的時候被創建,雖然方法區是堆的邏輯組成部分,但是簡單的虛擬機實現
可以選擇在這個區域不實現垃圾收集。這個版本的
Java
虛擬機規范也不限定實現方法區的內存位
置和編譯代碼的管理策略。方法區的容量可以是固定大小的,也可以隨著程序執行的需求動態擴展,
并在不需要過多空間時自動收縮。 方法區在實際內存空間中可以是不連續的。
Java
虛擬機實現應當提供給程序員或者最終用戶調節方法區初始容量的手段,對于可以動態
擴展和收縮方法區來說,則應當提供調節其最大、最小容量的手段。
方法區可能發生如下異常情況:
?
如果方法區的內存空間不能滿足內存分配請求,那
Java
虛擬機將拋出一個
OutOfMemoryError
異常。
運行時常量池
運行時常量池(
Runtime Constant Pool
)是每一個類或接口的常量池(
Constant_Pool
)的運行時表示形式,它包括了若干種不同的常量:從編譯期可知的數值字面量到必須運行
期解析后才能獲得的方法或字段引用。運行時常量池扮演了類似傳統語言中符號表(
Symbol
Table
)的角色,不過它存儲數據范圍比通常意義上的符號表要更為廣泛。
每一個運行時常量池都分配在
Java
虛擬機的
方法區
之中
,在類和接口被加載到
虛擬機后,對應的運行時常量池就被創建出來。
在創建類和接口的運行時常量池時,可能會發生如下異常情況:
* 當創建類或接口的時候,如果構造運行時常量池所需要的內存空間超過了方法區所能提供的最
大值,那
Java
虛擬機將會拋出一個
OutOfMemoryError
異常。
本地方法棧
? ? ? ?Java
虛擬機實現可能會使用到傳統的棧(通常稱之為“
C Stacks
”)來支持
native
方法
( 指使用
Java
以外的其他語言編寫的方法)的執行,這個棧就是本地方法棧(
Native Method
Stack
)。當
Java
虛擬機使用其他語言(例如
C
語言)來實現指令集解釋器時,也會使用到本地
方法棧。如果
Java
虛擬機不支持
natvie
方法,并且自己也不依賴傳統棧的話,可以無需支持本
地方法棧,如果支持本地方法棧,那這個棧一般會在線程創建的時候按線程分配。
? ? ? ?Java
虛擬機規范允許本地方法棧被實現成固定大小的或者是根據計算動態擴展和收縮的。如
果采用固定大小的本地方法棧,那每一條線程的本地方法棧容量應當在棧創建的時候獨立地選定。
一般情況下,
Java
虛擬機實現應當提供給程序員或者最終用戶調節虛擬機棧初始容量的手段,對
于長度可動態變化的本地方法棧來說,則應當提供調節其最大、最小容量的手段。
本地方法??赡馨l生如下異常情況:
?
如果線程請求分配的棧容量超過本地方法棧允許的最大容量時,
Java
虛擬機將會拋出一個
StackOverflowError
異常。
?
如果本地方法??梢詣討B擴展,并且擴展的動作已經嘗試過,但是目前無法申請到足夠的內存
去完成擴展,或者在建立新的線程時沒有足夠的內存去創建對應的本地方法棧,那
Java
虛擬
機將會拋出一個
OutOfMemoryError
異常。
? 棧幀

? 棧幀( Frame )是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態鏈接
(
Dynamic Linking
)、方法返回值和異常分派(
Dispatch Exception
)。
棧幀隨著方法調用而創建,隨著方法結束而銷毀——無論方法是正常完成還是異常完成(拋出
了在方法內未被捕獲的異常)都算作方法結束。棧幀的存儲空間分配在
Java
虛擬機棧
之中,每一個棧幀都有自己的
局部變量表
(
Local Variables
)、
操作數棧
(
Operand
Stack
)和指向當前方法所屬的類的
運行時常量池
的引用。
局部變量表和操作數棧的容量是在編譯期確定,并通過方法的
Code
屬性(
§
4.7.3
)保存及
提供給棧幀使用。因此,棧幀容量的大小僅僅取決于
Java
虛擬機的實現和方法調用時可被分配的
內存。
在一條線程之中,只有目前正在執行的那個方法的棧幀是活動的。這個棧幀就被稱為是當前棧
幀(
Current Frame
),這個棧幀對應的方法就被稱為是當前方法(
Current Method
),定義
這個方法的類就稱作當前類(
Current Class
)。對局部變量表和操作數棧的各種操作,通常都
指的是對當前棧幀的對局部變量表和操作數棧進行的操作。
如果當前方法調用了其他方法,或者當前方法執行結束,那這個方法的棧幀就不再是當前棧幀
了。當一個新的方法被調用,一個新的棧幀也會隨之而創建,并且隨著程序控制權移交到新的方法
而成為新的當前棧幀。當方法返回的之際,當前棧幀會傳回此方法的執行結果給前一個棧幀,在方
法返回之后,當前棧幀就隨之被丟棄,前一個棧幀就重新成為當前棧幀了。
請讀者特別注意,棧幀是線程本地私有的數據,不可能在一個棧幀之中引用另外一條線程的棧
幀。
局部變量表
操作數棧
動態鏈接
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
