java虛擬機(jī)
- 作者:新網(wǎng)
- 來(lái)源:新網(wǎng)
- 瀏覽:100
- 2018-05-14 10:11:47
相信很多人在從事java不久都會(huì)對(duì)JVM有種神秘感以及對(duì)那些人的膜拜。每當(dāng)說(shuō)起JVM大概都會(huì)想到Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū),那么它是怎樣劃分的呢?那么今天小編就來(lái)談?wù)勎覍?duì)它的理解。
相信很多人在從事java不久都會(huì)對(duì)JVM有種神秘感以及對(duì)那些人的膜拜。每當(dāng)說(shuō)起JVM大概都會(huì)想到Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū),那么它是怎樣劃分的呢?那么今天小編就來(lái)談?wù)勎覍?duì)它的理解。
<
div> 復(fù)制算法:它的出現(xiàn)就是為了解決標(biāo)記清除的不足,套路就是將內(nèi)存劃分為兩個(gè)等量大小的塊兒,對(duì)象都在其中一塊兒上,當(dāng)這一塊兒造完了就將存活的對(duì)象復(fù)制到另一塊兒上,然后將剛剛那塊兒一次清理掉,這樣就不需要考慮內(nèi)存碎片問(wèn)題,動(dòng)動(dòng)指針按順序非配就搞定了,實(shí)現(xiàn)簡(jiǎn)單效率高,但是代價(jià)有點(diǎn)大內(nèi)存直接干了一半,適用于對(duì)象存活率低的區(qū)域,比如朝生夕死的新生代。
標(biāo)記-整理算法:復(fù)制算法看起來(lái)很吊,但是對(duì)于對(duì)象存活率高的區(qū)域就顯得力不從心了,而且如果不想浪費(fèi)一半的
空間的話(huà),就需要進(jìn)行空間分配擔(dān)保(抵押貸款),所以老年代不能這么搞,進(jìn)而出現(xiàn)了標(biāo)記-整理算法,套路跟標(biāo)記-清除一樣,只是不直接清理可回收的對(duì)象,而是存活的往一邊兒移動(dòng),然后根據(jù)分界線去干掉另一邊兒,可以看出該算法要進(jìn)行對(duì)象的移動(dòng),成本相對(duì)略高,但好處則是不會(huì)產(chǎn)生內(nèi)存碎片。
方法區(qū)
方法區(qū)多數(shù)人認(rèn)為的永久代,方法區(qū)與堆一樣是線程共享的內(nèi)存區(qū)域,類(lèi)使用要經(jīng)過(guò)加載、連接(驗(yàn)證、準(zhǔn)備、解析)和初始化,加載后的類(lèi)信息就存在方法區(qū)特定的數(shù)據(jù)結(jié)構(gòu)中,主要包括:類(lèi)的全路徑名包括超類(lèi)(如果這個(gè)類(lèi)是Object則它沒(méi)有超類(lèi))、類(lèi)的類(lèi)型、類(lèi)的訪問(wèn)修飾符、直接接口全限定名的有序列表、運(yùn)行時(shí)常量池(類(lèi)版本、字段、方法信息、常量、類(lèi)靜態(tài)變量、裝載器信息) 等等。由于線程都共享方法區(qū),所以方法區(qū)的數(shù)據(jù)必須時(shí)線程安全的,如果有2個(gè)甚至多個(gè)線程同時(shí)訪問(wèn)某個(gè)類(lèi),而這類(lèi)又沒(méi)被JVM加載,那么JVM只允許一個(gè)線程去加載(雙親委派),其它線程必須等待。方法區(qū)的內(nèi)存不一定是連續(xù)的,可以動(dòng)態(tài)擴(kuò)展大小,可以選擇不實(shí)現(xiàn)GC,GC的目標(biāo)主要是常量池的回收和類(lèi)型的卸載,所以想想就好沒(méi)多少便宜可撿,因?yàn)榛厥諚l件比較苛刻,當(dāng)方法區(qū)無(wú)法滿(mǎn)足內(nèi)存分配需求時(shí)將OOM(String.intern()是個(gè)好例子)。
程序計(jì)數(shù)器
程序計(jì)數(shù)器屬于線程私有的,它是當(dāng)前線程所執(zhí)行字節(jié)碼的指示器(執(zhí)行到那兒了),它是一塊較小的內(nèi)存空間,線程下一步該干撒就是通過(guò)字節(jié)碼解釋器改變計(jì)數(shù)器來(lái)執(zhí)行的,每個(gè)線程都有自己的程序計(jì)數(shù)器,多線程就是輪流切換它來(lái)實(shí)現(xiàn),Java方法記錄的是虛擬機(jī)字節(jié)碼指令地址,Native方法沒(méi)有記錄,程序計(jì)數(shù)器在JVM中是唯一一個(gè)沒(méi)有定義OOM的區(qū)域。
虛擬機(jī)棧
如程序計(jì)數(shù)器一樣,Java虛擬機(jī)棧也屬于線程私有,所以它的生命周期與線程一樣。它屬于Java方法執(zhí)行的內(nèi)存模型,每個(gè)方法執(zhí)行都會(huì)創(chuàng)建一個(gè)棧幀,主要存儲(chǔ)著方法出口信息、局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接。當(dāng)線程請(qǐng)求的棧幀深度大于虛擬機(jī)所允許的深度會(huì)SOF,若虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存會(huì)OOM。
方法出口信息:正常方法返回時(shí)可能需要在棧幀中保存一些信息,用來(lái)幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài),如果有返回值,則把它壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令,若方法異常退出,那么返回地址是通過(guò)異常處理器來(lái)確定的,棧幀中一般不會(huì)保存這部分信息。
局部變量表:所需的內(nèi)存空間在編譯期確定,一旦確定無(wú)法更改大小,它存放著編譯期的各種基本數(shù)據(jù)類(lèi)型、reference類(lèi)型(可能是對(duì)象引用指針,也可能是個(gè)句柄)、returnAddress類(lèi)型(指向某條字節(jié)碼指令的地址)。
操作數(shù)棧:棧幀剛創(chuàng)建時(shí),操作數(shù)棧是沒(méi)有數(shù)據(jù)的,當(dāng)執(zhí)行方法操作時(shí),會(huì)存放從局部變量表復(fù)制的常量或者變量,包括方法入?yún)⒑头祷刂担僮鲾?shù)棧都一個(gè)固定的棧深度,入棧按先進(jìn)后出方式,最大深度由編譯期確定,基本類(lèi)型除了long,double用2個(gè)深度,其他都用一個(gè)。
動(dòng)態(tài)鏈接:class的常量池中存在有大量的符號(hào)引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用為參數(shù),這些符號(hào)引用分為兩種,一種就是類(lèi)加載的時(shí)候,靜態(tài)解析的那些final 和static代碼塊,得到的直接引用,還有一種是運(yùn)行期間轉(zhuǎn)化的(每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用),這種就是動(dòng)態(tài)鏈接。
本地方法棧
跟虛擬機(jī)棧的作用是一個(gè)屌樣,唯一區(qū)別就是虛擬機(jī)棧是為字節(jié)碼服務(wù)的,而它是為Native方法服務(wù),與虛擬機(jī)棧一樣,當(dāng)線程請(qǐng)求的棧幀深度大于虛擬機(jī)所允許的深度會(huì)SOF,若虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存會(huì)OOM。
直接內(nèi)存
Direct Memory 雖然不屬于虛擬機(jī)運(yùn)行數(shù)據(jù)區(qū),但在被NIO引入后一直頻繁使用(比如堆外緩存),可以用Native方法直接分配堆外內(nèi)存,然后在堆中去引用這塊兒區(qū)域(DirectByteBuffer就是),如果動(dòng)態(tài)擴(kuò)展內(nèi)存時(shí)達(dá)到物理內(nèi)存限制會(huì)OOM。
內(nèi)存分配策略以及類(lèi)加載機(jī)制以后再補(bǔ),先寫(xiě)到這兒吧,未完待續(xù)!