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

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

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