Java運行時數(shù)據(jù)區(qū)域

運行時數(shù)據(jù)區(qū)域

Java虛擬機在執(zhí)行Java程序的過程中會把它管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而存在, 有些區(qū)域則是依賴用戶線程的啟動和結(jié)束而建立和銷毀。如圖


1.1 程序計數(shù)器

是一塊內(nèi)存比較小的空間。作用:用來標志當前線程所執(zhí)行的字節(jié)碼的行號指示器(即在字節(jié)碼中添加編號)。在jvm中,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器上的值來選取下一條需要執(zhí)行的字節(jié)碼指令。在分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等功能都需要依賴計數(shù)器去完成。在多線程中, 各條線程之間的計數(shù)器互不影響,獨立存儲。

不會存在OutOfMemoryError情況的區(qū)域。

1.2 Java虛擬機棧

Java虛擬機棧也是線程私有的, 生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等,并且每個方法被調(diào)用直至執(zhí)行完成的過程就對應(yīng)著一個棧幀在虛擬機棧中從入棧到出棧的過程。

先理解什么是棧幀:是用于支持虛擬機進行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)。棧幀是基于虛擬機棧上的,一個虛擬機棧存儲了多個棧幀,每個棧幀記錄Java方法中局部標量表、操作數(shù)棧等信息同時也記錄該方法從入棧到出棧的過程。存儲結(jié)構(gòu)如圖


在編譯程序代碼的時候,棧幀中需要多大的局部變量表,多深的操作數(shù)棧都已經(jīng)完全確定,并且寫入到方法表的Code屬性之中,因此一個棧幀需要分配多少內(nèi)存,不會受到程序運行期變量數(shù)據(jù)的影響,而僅僅取決于具體的虛擬機實現(xiàn)。

局部變量表: 是一組變量值的存儲空間,用于存放方法參數(shù)、局部變量和對象引用類型。在編譯程序代碼后就確定需要局部變量的最大容量。變量槽是局部變量表中的最小單位。一個solt可以存放(x32)boolean、byte、char、short、int、float、reference 和 returnAddress(執(zhí)行字節(jié)碼指令的地址);對于64位的long和double變量則需要分配兩個連續(xù)的slot空間。為了節(jié)省??臻gSlot是可以重用的。不過有時也會影響垃圾回收行為。

從圖一和圖二中可以看出,圖二中變量槽是可以重用的,變量a復(fù)用了placeholder的slot,導(dǎo)致placheholder的引用被刪除,內(nèi)存被回收。

操作數(shù)棧:常稱為操作棧是一個后入先出棧。其最大的棧深度也在編譯過程中就確定了并保存在Class文件的Code屬性中。應(yīng)用場景:在方法進行參數(shù)傳遞的時候是通過操作數(shù)棧進行的。 在概念模型中,兩個棧幀作為虛擬機棧的元素, 相互之間是完全獨立的, 但是大多數(shù)虛擬機的實現(xiàn)里都會做些優(yōu)化處理,使得兩個棧幀出現(xiàn)一部分重疊。讓下棧幀的部分操作與上面棧幀的部分局部變量表重疊在一起,這樣在進行方法調(diào)用返回時就可以共用一分部數(shù)據(jù),而無需進行額外的參數(shù)復(fù)制傳遞了。

動態(tài)連接:每個棧幀都包含一個執(zhí)行運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接。Class文件中存放了大量的符號引用,字節(jié)碼中的方法調(diào)用執(zhí)行就是以常量池中指向方法的符號引用作為參數(shù)。這些符號引用一部分會在類加載階段或第一次使用時轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化稱為靜態(tài)解析。另一部分將在每一次運行期間轉(zhuǎn)化為直接引用,這部分稱為動態(tài)連接。

方法出口:方法出口分為兩種, 一種收到返回命令,正常退出;第二種執(zhí)行遇到異常導(dǎo)致方法退出。

虛擬機棧會出現(xiàn)兩種異常:StackOverflowError和OutOfMemoryError

1.3 本地方法棧

本地方法棧與虛擬機棧作用非常相似,其區(qū)別不過是虛擬機棧為執(zhí)行Java方法(字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機使用到的Native方法服務(wù)。

Native方法:并不一定指Java中用native方法(如String.intern() :native方法),也包括其他庫或者其他語言中的方法如C、C++。

本地方法棧也會出現(xiàn)兩種異常:StackOverflowError和OutOfMemoryError

1.4 Java堆

Java堆是Java虛擬機所管理的內(nèi)存中最大的一塊。是被所有線程共享的一塊內(nèi)存區(qū)域,虛擬機啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例。幾乎所有的對象實例都在這里分配內(nèi)存,但隨著jit編譯器的發(fā)展會有微妙的變化。

Java heap 是垃圾收集器管理的主要區(qū)域。在Java中,堆被劃分成兩個不同的區(qū)域:新生代(Young)和老年代(Old)。新生代又被劃分為三個區(qū)域:Eden空間、From Survivor空間、To Survivor空間。這樣劃分的目的是為了更好地管理內(nèi)存中的對象,包括內(nèi)存的分配以及回收。堆的內(nèi)存模型如圖(jdk1.6)


從圖中可以看出:堆大小=新生代+老年代。其中堆大小可以通過參數(shù)-Xms、-Xmx來設(shè)置。

Java 堆會有OutOfMemoryError異常

1.5 方法區(qū)

方法區(qū)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,但是存儲的內(nèi)容不同。方法區(qū)用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯后的代碼等數(shù)據(jù)。別名Non-Heap. 另外方法區(qū)并不是連續(xù)的,所以垃圾收集行為在這個區(qū)域是很少出現(xiàn)的。

方法區(qū)還包括一部分:常量池。常量池是用于存放編譯器生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運行時常量池中。大小可以通過-XX:PermSize和-XX:MaxPermSize設(shè)置。

方法區(qū)會有OutOfMemoryError異常

總結(jié):

1、區(qū)分下java棧、java堆和方法區(qū)分別存儲內(nèi)容:

public class Demo{

private String name ;

public Demo(String name){

this.name = name;

}

public static void main(String[] args){

Demo d = new Demo("test");

}

}

java堆          方法區(qū)         java棧

new Demo()     Demo類信息    ? ? ? ?Demo d、局部變量name

    ? ? ? ? ? ? ? ? ? ? Demo中的方法名   ? ? ? ?調(diào)用main方法主線程調(diào)用棧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容