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

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

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