github上的地址:DevelopBlog
概覽
java虛擬機(jī)(以下簡(jiǎn)稱JVM)多種多樣,其中都必須遵循《java虛擬機(jī)規(guī)范》的要求,本篇文章只討論hotspot
(SE7).
JVM在運(yùn)行程序時(shí),會(huì)把內(nèi)存劃分為幾個(gè)不同的區(qū)域,以方便線程的切換,GC,內(nèi)存的高效利用等,java運(yùn)行時(shí)數(shù)據(jù)區(qū)域示意圖如下:
其中
方法區(qū)
和堆
為線程共享的區(qū)域
程序計(jì)數(shù)器
首先了解一下.class
文件結(jié)構(gòu):
當(dāng)我們使用javac
命令將.java
文件編譯為.class
文件之時(shí),編譯器將類的各項(xiàng)信息按照《java虛擬機(jī)規(guī)范》中的規(guī)范一次寫(xiě)入.class
文件,其中包括ava版本信息,包名,類信息,字段信息,方法信息等。其中方法塊內(nèi)容將被編譯成為可被JVM識(shí)別的一條條字節(jié)碼指令
。
JVM執(zhí)行某一方法時(shí)咱台,加載.class
文件之后,JVM的字節(jié)碼解釋器讀取文件中的操作指令(一條指令包含操作碼與操作數(shù)),讀取一條執(zhí)行一條俭驮,在多線程任務(wù)不斷切換中回溺,如何才能記錄下各個(gè)線程的切換前的某一方法執(zhí)行的進(jìn)度呢?程序計(jì)數(shù)器
就是為此而生混萝,用來(lái)記錄正在執(zhí)行的JVM的字節(jié)碼的指令地址遗遵。由于它記錄的是某一條線程的執(zhí)行進(jìn)度,故他也是線程獨(dú)享的逸嘀。
虛擬機(jī)棧
此部分即我們經(jīng)常提起的java堆棧
中的棧
车要。他的生性周期與線程相同,虛擬機(jī)中存儲(chǔ)是以棧幀
為單位的崭倘,棧幀是虛擬機(jī)棧的的元素翼岁,在JVM執(zhí)行一新的方法時(shí),JVM會(huì)創(chuàng)建一個(gè)棧幀司光,該棧幀入棧琅坡,方法執(zhí)行結(jié)束后,該棧幀出残家。
棧幀用來(lái)存儲(chǔ)線程執(zhí)行過(guò)程時(shí)方法中的局部變量表榆俺,操作數(shù)棧,方法出口等其他信息跪削。
局部變量表即是我們經(jīng)常討論的部分谴仙,他存儲(chǔ)了基礎(chǔ)類型(int,double碾盐,boolean等),對(duì)象引用(不是對(duì)象本身揩局,而是一個(gè)對(duì)象的內(nèi)存地址)和returnAddress地址(前面提到的字節(jié)碼指令的地址)毫玖。
如圖所示:
本地方法棧
與虛擬機(jī)棧
類似付枫,區(qū)別在于它用來(lái)在虛擬機(jī)執(zhí)行Native方法時(shí)使用
堆
java開(kāi)發(fā)中經(jīng)常提及的烹玉,JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)域中最大的一塊,它的唯一作用就是存放實(shí)例化后的對(duì)象以及數(shù)組阐滩。同時(shí)也是GC
的主要區(qū)域二打。虛擬機(jī)棧中存儲(chǔ)了堆中對(duì)象的指針地址。
GC
垃圾回收采用分代的思想來(lái)管理內(nèi)存掂榔,在內(nèi)存回收的角度來(lái)看继效,堆分為老年代
和新生代
。
新生代是指新創(chuàng)建的對(duì)象装获,老年代是指存活時(shí)間比較長(zhǎng)瑞信,經(jīng)過(guò)多輪GC任然存活的對(duì)象。
先介紹一下兩種GC類型:
新生代GC(Minor GC)
:指發(fā)生在新生代的垃圾回收凡简,因?yàn)閖ava中大多數(shù)的java對(duì)象存活時(shí)間都不會(huì)很長(zhǎng),具備朝生夕滅
的特點(diǎn)精肃,所以Minor GC
的回收速度特別快秤涩,也特別頻繁。老年代GC(Major GC/Full GC)
:指發(fā)生帶老年代的GC司抱,大多數(shù)情況下會(huì)伴隨發(fā)生至少一次的新生代GC(Minor GC)溉仑,由于使用不同的垃圾回收算法,故而回收速度非常慢状植。(關(guān)于GC的算法以及常見(jiàn)垃圾回收器浊竟,將在另一篇文章中講解)
新生代是如何晉升為老年代呢?
參照上圖:
Survivor
分為兩個(gè)區(qū)域津畸,被稱為S0
和S1
振定,兩個(gè)區(qū)域總有一個(gè)是空閑的。
步驟如下:
新出生的對(duì)象優(yōu)先存活在
Eden
區(qū)域(Eden
不夠時(shí)肉拓,發(fā)起一次Minor GC
)后频,java在每個(gè)對(duì)象創(chuàng)建時(shí),為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器暖途,用于標(biāo)記一個(gè)對(duì)象的存活時(shí)間卑惜。第一次
Minor GC
新生代GC發(fā)生之后,如果該對(duì)象仍然存活驻售,他的年齡+1露久,并將它從Eden
移至Survivor
中deS0
區(qū)域(這時(shí)Eden
和S1
區(qū)域?yàn)榭眨?/p>
*當(dāng)再次發(fā)生 Minor GC
時(shí),此時(shí)回收的區(qū)域?yàn)?code>Eden和S0
區(qū)域欺栗,此時(shí)S0
區(qū)域沒(méi)有被回收的對(duì)象年齡+1毫痕。此時(shí)重點(diǎn)來(lái)了征峦!S0
對(duì)象有兩個(gè)去處,如果達(dá)到最大年齡(默認(rèn)15)的對(duì)象直接移動(dòng)到老年代中消请,沒(méi)有到達(dá)年齡的對(duì)象的對(duì)象全部移至S1
區(qū)域,S0
清空栏笆;而Eden
和上一步驟同樣,存活對(duì)象移至S1
如下圖:
此次GC完成之后,S0
和Eden
為空 如下圖
此后臊泰,
再次發(fā)生GC之后蛉加,Eden
和S1
發(fā)生回收,將對(duì)象移至S0
中缸逃,完成之后针饥,Eden
和S1
為空;
再次發(fā)生GC之后察滑,Eden
和S0
發(fā)生回收打厘,將對(duì)象移至S1
中,完成之后贺辰,Eden
和S0
為空户盯;
依此循環(huán),直至年齡達(dá)到一定程度(默認(rèn)15)饲化,對(duì)象進(jìn)入老年代莽鸭。
年齡最大值默認(rèn)為15, 可以通過(guò)參數(shù) -XX MaxTenuringThreshold 設(shè)置
方法區(qū)
方法區(qū)
他存儲(chǔ)的是已經(jīng)被JVM加載的類信息吃靠,常量池硫眨,靜態(tài)變量,JVM即使編譯器等數(shù)據(jù)巢块,同樣也是線程共享區(qū)域礁阁。他經(jīng)常被看成是堆
的邏輯部分,為了方便這部分內(nèi)存管理族奢,JVM將垃圾回收范圍擴(kuò)展至方法區(qū)姥闭,在GC
中被稱為永久區(qū)
(上圖中的Permanent
),故而他經(jīng)常被稱為非堆
越走,與java堆進(jìn)行區(qū)分棚品。
GC對(duì)此部分的回收主要集中在對(duì)常量池的回收以及類型的卸載。
常量池
常量池是方法區(qū)的一部分廊敌,用于存放由javac
編譯時(shí)生成的.class
文件中的常量(例如:包名铜跑,類名,方法名骡澈,字段名锅纺,String字符串等,如下圖)急膀。
著作權(quán)歸作者所有聚请。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)膛壹,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處涡拘。