Inside JVM體系結(jié)構(gòu)詳解
當(dāng)Inside JVM運(yùn)行程序時,字節(jié)碼,創(chuàng)建的對象,傳遞給方法的參數(shù),返回值,局部變量以及運(yùn)算的中間結(jié)果保存在運(yùn)行時數(shù)據(jù)區(qū)中。規(guī)范本身對運(yùn)行時數(shù)據(jù)區(qū)只有抽象的描述,也使得JVM可以容易的在各種計算機(jī)和設(shè)備上實(shí)現(xiàn)。
簡單介紹Inside JVM體系結(jié)構(gòu)
JVM的體系結(jié)構(gòu)根據(jù)JVM的規(guī)范可分為類裝載子系統(tǒng),運(yùn)行時數(shù)據(jù)區(qū),執(zhí)行引擎。本文將為大家介紹Inside JVM體系結(jié)構(gòu)。當(dāng)Inside JVM運(yùn)行程序時,字節(jié)碼,創(chuàng)建的對象,傳遞給方法的參數(shù),返回值,局部變量以及運(yùn)算的中間結(jié)果保存在運(yùn)行時數(shù)據(jù)區(qū)中。規(guī)范本身對運(yùn)行時數(shù)據(jù)區(qū)只有抽象的描述,也使得JVM可以容易的在各種計算機(jī)和設(shè)備上實(shí)現(xiàn)。
Inside JVM運(yùn)行時數(shù)據(jù)區(qū)
1方法區(qū):
JVM中被裝載的類型信息存儲在一個邏輯上被稱為方法區(qū)的內(nèi)存中,JVM在裝載完CLASS文件后提取其中的類型信息并將之存儲在方法區(qū)。該類型的靜態(tài)變量同樣也存儲在方法區(qū)中。由于所有的線程共享方法區(qū),所以對方法區(qū)數(shù)據(jù)的訪問必須考慮到線程的同步。
方法區(qū)的大小是不固定的,JVM可以通過裝載新類型或者卸載已有類型來動態(tài)的調(diào)整方法區(qū)的大小,即可以改變方法區(qū)所占用的內(nèi)存。方法區(qū)不一定是連續(xù)的,方法區(qū)可以在一個堆中自由分配。
JVM保存的在方法區(qū)中的存儲類型以下信息
此類型的全限定名
此類型的直接超類的全限定名(除非這個類型為java.lang.object,它沒有超類)
此類型為接口還是類
此類型的訪問修飾符號(public,abstact或final的某個子集)
除以上基本信息,還得存儲以下類型的具體信息
此類型的常量池
字段信息
方法信息
除常量以外的所有靜態(tài)變量
一個到類classLoader的引用
一個到Class類的引用
常量池可以理解為本地指針數(shù)組,在JAVA的動態(tài)連接中起核心的左右,后邊再做詳細(xì)的介紹。
為了盡可能的提高訪問的效率,必須優(yōu)化存儲在方法區(qū)中的類型信息的數(shù)據(jù)結(jié)構(gòu)。所以,實(shí)現(xiàn)中還可以加如其他數(shù)據(jù)結(jié)構(gòu)以加快訪問原始數(shù)據(jù)的速度,如方法表。JVM為每個裝載的非抽象類,都生成一個方法表,把他作為類信息的一部分保存在方法區(qū)。方法表也是一個本地指針數(shù)組,其元素為方法的入口地址。方法表所指向?qū)嵗椒ǖ臄?shù)據(jù)包括以下信息:
此方法的操作數(shù)棧和局部變量的大小
此方法的字節(jié)碼
異常表
JVM可以為每個對象生成一份方法表的copy(這樣比較耗內(nèi)存,但能提高訪問的速度)或只在對象里保存到方法區(qū)中方法表的引用。這和C++中的VTBL很象,在C++中,對象有實(shí)例數(shù)據(jù)和一組指向?qū)ο罂梢哉{(diào)用的虛擬函數(shù)指針組成。
2堆
Java程序在運(yùn)行時創(chuàng)建的所有類室例或數(shù)組都放在同一個堆中,而一個JVM實(shí)例只有一個堆空間,所有線程都共享這個堆。堆空間可以自由的伸縮,也不必是連續(xù)的。
常見的堆空間的設(shè)計:
a.把堆空間分為兩部分:句柄池和對象池,對象的引用為指向句柄池的本地指針,句柄池里的每個條目分為兩部分,一部分為指向?qū)ο蟪氐谋镜刂羔?,一部分為指向方法區(qū)類數(shù)據(jù)區(qū)的本地指針。對象池里保存的是實(shí)例對象的數(shù)據(jù),此數(shù)據(jù)是實(shí)例私有的。這種設(shè)計的好處有利于內(nèi)存碎片的整理,當(dāng)移動對象池中的對象時,句柄部分只需要修改指向?qū)ο蟪貤l目的地址。缺點(diǎn)就是兩級指針的訪問。
b.使對象指針直接的指向?qū)ο髷?shù)據(jù),該數(shù)據(jù)包括指向方法區(qū)方法區(qū)數(shù)據(jù)類類型的指針和對象的實(shí)例數(shù)據(jù)。這樣的優(yōu)缺點(diǎn)正好和前邊的方法相反。當(dāng)移動堆中的對象時,對象的指針也得跟著改變,這就必須在整個運(yùn)行時數(shù)據(jù)區(qū)中更新被移動對象的引用。
以上兩種方法的思想可以類比為對鏈表和數(shù)組的刪除和加入操作。
在Java中,數(shù)組和其他對象一樣,總是存儲在堆中并擁有一個與他們的類向關(guān)聯(lián)的CLASS實(shí)例,所有具有相同維度和類型的數(shù)組都是一個類的實(shí)例,而不管數(shù)組的長度。
3程序記數(shù)器
每個線程都有自己的程序記數(shù)器,它的內(nèi)容總是下一條將被執(zhí)行指令的地址。
4Java棧
當(dāng)一個線程被創(chuàng)建時都將得到自己的程序記數(shù)器和Java棧,Java棧以幀為單位保存調(diào)用信息。當(dāng)線程調(diào)用一個方法時,JVM會壓如一新的棧幀到Java棧,反之則彈出。也就是說,JVM只會對Java棧執(zhí)行兩種操作:以幀為單位的壓棧和彈棧。幀的大小根據(jù)調(diào)用信息是可變的,后邊做詳細(xì)的介紹。由于Java棧上的數(shù)據(jù)是此線程私有的,因此不需考慮多線程下的棧數(shù)據(jù)的線程安全問題。
Inside JVM棧幀由三部分組成:局部變量區(qū),操作數(shù)棧和幀數(shù)據(jù)區(qū)。
a.局部變量區(qū)
局部變量區(qū)的大小由調(diào)用方法的參數(shù)和方法的局部變量所決定。編譯器按聲名順序?qū)⑺麄兎诺骄植孔兞繑?shù)組,此數(shù)組以字長為單位,從0開始記數(shù)。如果是實(shí)例方法,數(shù)組的第一個元素為實(shí)例的this指針。
在Java中,所有的對象都按引用傳遞,并且對象存儲在堆中,在局部變量或操作數(shù)棧中不會有對象的COPY,只有對象引用。
b操作數(shù)棧
操作數(shù)棧也是以字長為單位的數(shù)組,但不同于局部變量數(shù)組以索引去訪問,它是通過標(biāo)準(zhǔn)的棧操作,壓棧和彈棧來訪問的。JVM沒有寄存器,程序記數(shù)器也無法被程序指令直接訪問。JVM的運(yùn)行方式是基于棧的而非基于寄存器的,JVM的指令是從操作數(shù)棧中而不是寄存器中取得操作數(shù)的。雖然指令也可以從其他地方取得操作數(shù),比如從字節(jié)碼流中跟隨在操作碼之后的字節(jié)中或從常量池中,但主要還是從操作數(shù)棧中獲取操作數(shù)。
JVM把操作數(shù)棧作為它的工作區(qū),大多數(shù)指令都要從這里彈出數(shù)據(jù),執(zhí)行運(yùn)算,然后把結(jié)果壓回操作數(shù)棧,然后等相關(guān)的指令將結(jié)果再次彈出。操作數(shù)棧扮演了暫存操作數(shù)的角色。
C幀數(shù)據(jù)區(qū)
除局部變量區(qū)和操作數(shù)棧外,Java棧幀還數(shù)據(jù)來支持常量池的解析、正常方法的返回以及異常派發(fā)機(jī)制。這些信息都保存在Java棧幀的幀數(shù)據(jù)區(qū)中。JVM可以通過幀數(shù)據(jù)區(qū)中指向常量池的指針來執(zhí)行某個需要用到常量池數(shù)據(jù)的指令。
5本地方法棧
當(dāng)線程調(diào)用本地方法時,JVM會保持Java棧不變,不再在線程的Java棧中壓入新的幀,Inside JVM只是簡單地動態(tài)連接并直接調(diào)用本地方法。但是,本地方法有可能回調(diào)JVM中的Java方法,此時該線程會保存本地方法棧的狀態(tài)并進(jìn)入Java棧,在Java棧壓入新的棧幀。
【編輯推薦】
- 簡單介紹Inside JVM體系結(jié)構(gòu)
- 解決JVM最大內(nèi)存設(shè)置問題
- 調(diào)用weblogic設(shè)置jvmheap大小
- 詳解Tomcat配置JVM參數(shù)步驟
- 深入學(xué)習(xí)JVM內(nèi)存設(shè)置原理和調(diào)優(yōu)