×

java虛擬機(jī)

  • 作者:新網(wǎng)
  • 來(lái)源:新網(wǎng)
  • 瀏覽:100
  • 2018-05-14 10:13:32

相信很多人在從事java不久都會(huì)對(duì)JVM有種神秘感以及對(duì)那些人的膜拜。每當(dāng)說(shuō)起JVM大概都會(huì)想到Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū),那么它是怎樣劃分的呢?那么今天小編就來(lái)談?wù)勎覍?duì)它的理解。

   t0162c4f7efaaf86d49.jpg

<div>  相信很多人在從事java不久都會(huì)對(duì)JVM有種神秘感以及對(duì)那些人的膜拜。每當(dāng)說(shuō)起JVM大概都會(huì)想到Java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū),那么它是怎樣劃分的呢?那么今天小編就來(lái)談?wù)勎覍?duì)它的理解。
  堆,首先第一個(gè)想到的就是它了,在虛擬機(jī)啟動(dòng)時(shí)就創(chuàng)建,一坨兒活躍在虛擬機(jī)所管理內(nèi)存中的巨無(wú)霸(內(nèi)存最大),被所有線程共享的內(nèi)存區(qū)域,該區(qū)域唯一的目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例以及數(shù)組都要在堆上分配,堆是虛擬機(jī)收集的主要區(qū)域,基本上都采用的是分代收集算法,分為:新生代、老年代,堆大小可通過(guò)-Xms、-Xmx控制,如果堆沒(méi)有內(nèi)存來(lái)完成實(shí)例分配且也無(wú)法再擴(kuò)展時(shí),將會(huì)OOM。
  新生代:新創(chuàng)建的對(duì)象在此分配,由于新生代中的對(duì)象屬于朝生夕死的,其回收算法采用的是復(fù)制算法,它由Eden區(qū)和兩個(gè)大小相等的From Survivor區(qū)、To Survivor區(qū)構(gòu)成。當(dāng)Minor GC時(shí),Eden區(qū)存活的對(duì)象將被移到To Survivor區(qū),而From Survivor區(qū)存活的對(duì)象根據(jù)其年齡決定,當(dāng)年齡達(dá)到閥值(-XX:MaxTenuringThreshold可配,沒(méi)記錯(cuò)的話默認(rèn)15歲就老了)則會(huì)進(jìn)入老年代,否則進(jìn)入To Survivor區(qū),虛擬機(jī)會(huì)清空Eden區(qū)和From Survivor區(qū),最后將To Survivo區(qū)與From Survivor區(qū)互換角色來(lái)保證To Survivo區(qū)為空。
  這里只說(shuō)下部分調(diào)整年輕代的配置,其它的大家可根據(jù)Sun文檔選擇合適自己的。
  1)-XX:NewSize、-XX:MaxNewSize 設(shè)置年輕代的大小,建議兩個(gè)值設(shè)為一樣大,一般為整個(gè)堆大小的1/3或者1/4。
  2)-XX:SurvivorRatio 設(shè)置Eden和Survivor的比值,默認(rèn)8:1:1。
  3)-XX:+PrintTenuringDistribution 用于顯示每次Minor GC時(shí)Survivor區(qū)中各個(gè)年齡段的對(duì)象的大小。
  4)-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold用于設(shè)置晉升到老年代的對(duì)象年齡的最小值和最大值,每扛過(guò)一次Minor GC之后,年齡就加1。
  5)-Xmn 設(shè)置年輕代大小 可代替1) 。整個(gè)JVM內(nèi)存大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會(huì)減小年老代大小。此值對(duì)系統(tǒng)性能影響較大,官方推薦配置為整個(gè)堆的3/8。
  老年代:存放的對(duì)象比較穩(wěn)定,主要存放著那些扛過(guò)了好幾次Minor GC回收仍然還活著或者特別大(XX:PretenureSizeThreshold 可配)的對(duì)象,當(dāng)老年代的連續(xù)空間無(wú)法分配給新進(jìn)入的較大對(duì)象時(shí),會(huì)觸發(fā)一次Full GC來(lái)騰出更多的空間,F(xiàn)ull GC的代價(jià)很高,會(huì)造成Stop the world,所以要減少Full GC次數(shù)。老年代GC采用的是標(biāo)記-清除或者標(biāo)記-整理算法(根據(jù)收集器選擇)。
  標(biāo)記-清除算法:Mark-Sweep顧名思義算法分為標(biāo)記和清除兩個(gè)步驟,虛擬機(jī)會(huì)先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收它們。標(biāo)記過(guò)程大概是:對(duì)對(duì)象進(jìn)行可達(dá)性分析后,發(fā)現(xiàn)木有與GC Roots相連接的引用鏈,將會(huì)第一次被標(biāo)記并會(huì)搞一次篩選活動(dòng),活動(dòng)內(nèi)容就是看看這個(gè)對(duì)象有沒(méi)有必要執(zhí)行finalize方法,如果沒(méi)有覆蓋這個(gè)方法或者已經(jīng)執(zhí)行過(guò)這個(gè)方法,那么虛擬機(jī)會(huì)認(rèn)為沒(méi)有必要去執(zhí)行。如果虛擬機(jī)認(rèn)為有必要執(zhí)行這個(gè)方法的話,這個(gè)對(duì)象就會(huì)去F-Queue里待著等待一個(gè)finalizer線程去執(zhí)行finalize方法,因?yàn)檫@個(gè)方法是脫離死亡的救命草;第二次標(biāo)記是GC對(duì)F-Queue進(jìn)行小規(guī)模標(biāo)記,如果對(duì)象在finalize方法抓住了那根草(與GC Roots引用鏈上的任何一個(gè)對(duì)像關(guān)聯(lián)即可),那么它將被移除即將回收的區(qū)域,如果沒(méi)有抓住那根草那么它基本靠別自行車了。標(biāo)記-清除算法有兩個(gè)不足點(diǎn),第一就是它的兩個(gè)過(guò)程效率都不高,另一個(gè)就是空間問(wèn)題,會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,會(huì)導(dǎo)致分配較大對(duì)象時(shí)無(wú)法申請(qǐng)到足夠的連續(xù)空間從而觸發(fā)一次GC。
  復(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)一半的空間的話,就需要進(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ū)域,類使用要經(jīng)過(guò)加載、連接(驗(yàn)證、準(zhǔn)備、解析)和初始化,加載后的類信息就存在方法區(qū)特定的數(shù)據(jù)結(jié)構(gòu)中,主要包括:類的全路徑名包括超類(如果這個(gè)類是Object則它沒(méi)有超類)、類的類型、類的訪問(wèn)修飾符、直接接口全限定名的有序列表、運(yùn)行時(shí)常量池(類版本、字段、方法信息、常量、類靜態(tài)變量、裝載器信息) 等等。由于線程都共享方法區(qū),所以方法區(qū)的數(shù)據(jù)必須時(shí)線程安全的,如果有2個(gè)甚至多個(gè)線程同時(shí)訪問(wèn)某個(gè)類,而這類又沒(méi)被JVM加載,那么JVM只允許一個(gè)線程去加載(雙親委派),其它線程必須等待。方法區(qū)的內(nèi)存不一定是連續(xù)的,可以動(dòng)態(tài)擴(kuò)展大小,可以選擇不實(shí)現(xiàn)GC,GC的目標(biāo)主要是常量池的回收和類型的卸載,所以想想就好沒(méi)多少便宜可撿,因?yàn)榛厥諚l件比較苛刻,當(dāng)方法區(qū)無(wú)法滿足內(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ù)類型、reference類型(可能是對(duì)象引用指針,也可能是個(gè)句柄)、returnAddress類型(指向某條字節(jié)碼指令的地址)。
  操作數(shù)棧:棧幀剛創(chuàng)建時(shí),操作數(shù)棧是沒(méi)有數(shù)據(jù)的,當(dāng)執(zhí)行方法操作時(shí),會(huì)存放從局部變量表復(fù)制的常量或者變量,包括方法入?yún)⒑头祷刂?,操作?shù)棧都一個(gè)固定的棧深度,入棧按先進(jìn)后出方式,最大深度由編譯期確定,基本類型除了long,double用2個(gè)深度,其他都用一個(gè)。
  動(dòng)態(tài)鏈接:class的常量池中存在有大量的符號(hào)引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用為參數(shù),這些符號(hào)引用分為兩種,一種就是類加載的時(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)存分配策略以及類加載機(jī)制以后再補(bǔ),先寫到這兒吧,未完待續(xù)!
 

免責(zé)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻(xiàn)自行上傳,本網(wǎng)站不擁有所有權(quán),也不承認(rèn)相關(guān)法律責(zé)任。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,請(qǐng)發(fā)送郵件至:operations@xinnet.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),本站將立刻刪除涉嫌侵權(quán)內(nèi)容。

免費(fèi)咨詢獲取折扣

Loading