JVM內(nèi)存分配和垃圾收集

參考:
周志明 《深入理解Java虛擬機(jī)》
http://www.cnblogs.com/dolphin0520/p/3613043.html

1 JVM內(nèi)存區(qū)域劃分

1. Java程序的執(zhí)行過(guò)程

首先奕短,Java源代碼文件(.java后綴)會(huì)被Java編譯器編譯為字節(jié)碼文件(.class后綴),然后由JVM中的類(lèi)加載器加載各個(gè)類(lèi)的字節(jié)碼文件蒲稳,加載完畢之后寄狼,交由JVM執(zhí)行引擎執(zhí)行冀续。在整個(gè)程序執(zhí)行過(guò)程中,JVM會(huì)用一段空間來(lái)存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間一般被稱(chēng)作為Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū))荚藻,也就是我們常說(shuō)的JVM內(nèi)存咽扇。因此邪财,在Java中我們常常說(shuō)到的內(nèi)存管理就是針對(duì)這段空間進(jìn)行管理(如何分配和回收內(nèi)存空間)。如圖所示:


QQ截圖20160301223335.png
2. 運(yùn)行時(shí)數(shù)據(jù)區(qū)的結(jié)構(gòu)

根據(jù)《Java虛擬機(jī)規(guī)范》的規(guī)定质欲,運(yùn)行時(shí)數(shù)據(jù)區(qū)通常包括這幾個(gè)部分:程序計(jì)數(shù)器(Program Counter Register)树埠、Java棧(VM Stack)、本地方法棧(Native Method Stack)嘶伟、方法區(qū)(Method Area)怎憋、堆(Heap)。

QQ截圖20160301223335.png
  1. 程序計(jì)數(shù)器
    程序計(jì)數(shù)器(Program Counter Register)九昧,也有稱(chēng)作為PC寄存器盛霎。想必學(xué)過(guò)匯編語(yǔ)言的朋友對(duì)程序計(jì)數(shù)器這個(gè)概念并不陌生,在匯編語(yǔ)言中耽装,程序計(jì)數(shù)器是指CPU中的寄存器愤炸,它保存的是程序當(dāng)前執(zhí)行的指令的地址(也可以說(shuō)保存下一條指令的所在存儲(chǔ)單元的地址),當(dāng)CPU需要執(zhí)行指令時(shí)掉奄,需要從程序計(jì)數(shù)器中得到當(dāng)前需要執(zhí)行的指令所在存儲(chǔ)單元的地址规个,然后根據(jù)得到的地址獲取到指令凤薛,在得到指令之后,程序計(jì)數(shù)器便自動(dòng)加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址诞仓,如此循環(huán)缤苫,直至執(zhí)行完所有的指令。
    雖然JVM中的程序計(jì)數(shù)器并不像匯編語(yǔ)言中的程序計(jì)數(shù)器一樣是物理概念上的CPU寄存器墅拭,但是JVM中的程序計(jì)數(shù)器的功能跟匯編語(yǔ)言中的程序計(jì)數(shù)器的功能在邏輯上是等同的活玲,也就是說(shuō)是用來(lái)指示 執(zhí)行哪條指令的。
    由于在JVM中谍婉,多線程是通過(guò)線程輪流切換來(lái)獲得CPU執(zhí)行時(shí)間的舒憾,因此,在任一具體時(shí)刻穗熬,一個(gè)CPU的內(nèi)核只會(huì)執(zhí)行一條線程中的指令镀迂,因此,為了能夠使得每個(gè)線程都在線程切換后能夠恢復(fù)在切換之前的程序執(zhí)行位置唤蔗,每個(gè)線程都需要有自己獨(dú)立的程序計(jì)數(shù)器探遵,并且不能互相被干擾,否則就會(huì)影響到程序的正常執(zhí)行次序妓柜。因此箱季,可以這么說(shuō),程序計(jì)數(shù)器是每個(gè)線程所私有的棍掐。
    在JVM規(guī)范中規(guī)定藏雏,如果線程執(zhí)行的是非native方法,則程序計(jì)數(shù)器中保存的是當(dāng)前需要執(zhí)行的指令的地址塌衰;如果線程執(zhí)行的是native方法诉稍,則程序計(jì)數(shù)器中的值是undefined。
    由于程序計(jì)數(shù)器中存儲(chǔ)的數(shù)據(jù)所占空間的大小不會(huì)隨程序的執(zhí)行而發(fā)生改變最疆,因此杯巨,對(duì)于程序計(jì)數(shù)器是不會(huì)發(fā)生內(nèi)存溢出現(xiàn)象(OutOfMemory)的。
  2. Java 棧
    Java棧也稱(chēng)作虛擬機(jī)棧(Java Vitual Machine Stack)努酸,也就是我們常常所說(shuō)的棧服爷,跟C語(yǔ)言的數(shù)據(jù)段中的棧類(lèi)似。事實(shí)上获诈,Java棧是Java方法執(zhí)行的內(nèi)存模型仍源。
    erer.png

    局部變量表,顧名思義舔涎,想必不用解釋大家應(yīng)該明白它的作用了吧笼踩。就是用來(lái)存儲(chǔ)方法中的局部變量(包括在方法中聲明的非靜態(tài)變量以及函數(shù)形參)。對(duì)于基本數(shù)據(jù)類(lèi)型的變量亡嫌,則直接存儲(chǔ)它的值嚎于,對(duì)于引用類(lèi)型的變量掘而,則存的是指向?qū)ο蟮囊谩>植孔兞勘淼拇笮≡诰幾g器就可以確定其大小了于购,因此在程序執(zhí)行期間局部變量表的大小是不會(huì)改變的袍睡。
    操作數(shù)棧,想必學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)中的棧的朋友想必對(duì)表達(dá)式求值問(wèn)題不會(huì)陌生肋僧,棧最典型的一個(gè)應(yīng)用就是用來(lái)對(duì)表達(dá)式求值斑胜。想想一個(gè)線程執(zhí)行方法的過(guò)程中,實(shí)際上就是不斷執(zhí)行語(yǔ)句的過(guò)程嫌吠,而歸根到底就是進(jìn)行計(jì)算的過(guò)程止潘。因此可以這么說(shuō),程序中的所有計(jì)算過(guò)程都是在借助于操作數(shù)棧來(lái)完成的居兆。
    指向運(yùn)行時(shí)常量池的引用覆山,因?yàn)樵诜椒▓?zhí)行的過(guò)程中有可能需要用到類(lèi)中的常量竹伸,所以必須要有一個(gè)引用指向運(yùn)行時(shí)常量泥栖。
    方法返回地址,當(dāng)一個(gè)方法執(zhí)行完畢之后勋篓,要返回之前調(diào)用它的地方吧享,因此在棧幀中必須保存一個(gè)方法返回地址。
    由于每個(gè)線程正在執(zhí)行的方法可能不同譬嚣,因此每個(gè)線程都會(huì)有一個(gè)自己的Java棧钢颂,互不干擾。
  3. 本地方法棧
    本地方法棧與Java棧的作用和原理非常相似拜银。區(qū)別只不過(guò)是Java棧是為執(zhí)行Java方法服務(wù)的殊鞭,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的。在JVM規(guī)范中尼桶,并沒(méi)有對(duì)本地方發(fā)展的具體實(shí)現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強(qiáng)制規(guī)定操灿,虛擬機(jī)可以自由實(shí)現(xiàn)它。在HotSopt虛擬機(jī)中直接就把本地方法棧和Java棧合二為一泵督。

  4. Java中的堆是用來(lái)存儲(chǔ)對(duì)象本身的以及數(shù)組(當(dāng)然趾盐,數(shù)組引用是存放在Java棧中的)。只不過(guò)和C語(yǔ)言中的不同小腊,在Java中救鲤,程序員基本不用去關(guān)心空間釋放的問(wèn)題,Java的垃圾回收機(jī)制會(huì)自動(dòng)進(jìn)行處理秩冈。因此這部分空間也是Java垃圾收集器管理的主要區(qū)域本缠。另外,堆是被所有線程共享的入问,在JVM中只有一個(gè)堆丹锹。
  5. 方法區(qū)
    方法區(qū)在JVM中也是一個(gè)非常重要的區(qū)域犹赖,它與堆一樣,是被線程共享的區(qū)域卷仑。在方法區(qū)中峻村,存儲(chǔ)了每個(gè)類(lèi)的信息(包括類(lèi)的名稱(chēng)、方法信息锡凝、字段信息)粘昨、靜態(tài)變量、常量以及編譯器編譯后的代碼等窜锯。
    在Class文件中除了類(lèi)的字段张肾、方法、接口等描述信息外锚扎,還有一項(xiàng)信息是常量池吞瞪,用來(lái)存儲(chǔ)編譯期間生成的字面量和符號(hào)引用。
    在方法區(qū)中有一個(gè)非常重要的部分就是運(yùn)行時(shí)常量池驾孔,它是每一個(gè)類(lèi)或接口的常量池的運(yùn)行時(shí)表示形式芍秆,在類(lèi)和接口被加載到JVM后,對(duì)應(yīng)的運(yùn)行時(shí)常量池就被創(chuàng)建出來(lái)翠勉。當(dāng)然并非Class文件常量池中的內(nèi)容才能進(jìn)入運(yùn)行時(shí)常量池妖啥,在運(yùn)行期間也可將新的常量放入運(yùn)行時(shí)常量池中,比如String的intern方法对碌。
    在JVM規(guī)范中荆虱,沒(méi)有強(qiáng)制要求方法區(qū)必須實(shí)現(xiàn)垃圾回收。很多人習(xí)慣將方法區(qū)稱(chēng)為“永久代”朽们,是因?yàn)镠otSpot虛擬機(jī)以永久代來(lái)實(shí)現(xiàn)方法區(qū)怀读,從而JVM的垃圾收集器可以像管理堆區(qū)一樣管理這部分區(qū)域,從而不需要專(zhuān)門(mén)為這部分設(shè)計(jì)垃圾回收機(jī)制骑脱。不過(guò)自從JDK7之后菜枷,Hotspot虛擬機(jī)便將運(yùn)行時(shí)常量池從永久代移除了。

3.垃圾收集

  1. 方法區(qū)垃圾收集:收集無(wú)用的類(lèi)和常量惜姐。判定一個(gè)類(lèi)已經(jīng)廢棄需要三個(gè)條件:
  • 該類(lèi)所有的實(shí)例已經(jīng)被回收
  • 加載該類(lèi)的ClassLoader已經(jīng)被回收
  • 該類(lèi)對(duì)應(yīng)的Class類(lèi)對(duì)象沒(méi)有被引用犁跪,沒(méi)有代碼通過(guò)反射訪問(wèn)該類(lèi)的方法。
  1. 判斷堆中的對(duì)象死亡:
  • 引用計(jì)數(shù)法:通過(guò)一個(gè)計(jì)數(shù)器來(lái)記錄對(duì)象被引用的次數(shù)歹袁,無(wú)法解決循環(huán)引用的問(wèn)題
  • 根搜索算法: 選取一些對(duì)象作為 GC-Roots坷衍, 進(jìn)行引用搜索,當(dāng)判定一個(gè)對(duì)象進(jìn)行根搜索無(wú)法到達(dá)的時(shí)候条舔,說(shuō)明該對(duì)象已經(jīng)沒(méi)有被引用枫耳。可作為GC-Roots的對(duì)象包括:
    a. 虛擬機(jī)棧中引用的對(duì)象
    b. 方法區(qū)中類(lèi)的靜態(tài)屬性引用的對(duì)象
    c. 方法區(qū)中常量引用的對(duì)象
    d. 本地方法棧中引用的對(duì)象
  • 兩次標(biāo)記:在根搜索中得到的沒(méi)有引用的對(duì)象不是立即被標(biāo)記成可回收對(duì)象的孟抗,而是先進(jìn)行一次標(biāo)記迁杨,假如對(duì)象覆蓋了finalize() 方法钻心,則放進(jìn)一個(gè)隊(duì)列F-Queue中,由一個(gè)虛擬機(jī)創(chuàng)建的低優(yōu)先級(jí)Finalizer執(zhí)行對(duì)象的finalize()方法铅协,如果對(duì)象的這個(gè)方法里成功建立了對(duì)自己的引用捷沸,則會(huì)逃離本次垃圾收集。需要注意的是狐史,每個(gè)對(duì)象的finalize()方法僅會(huì)被執(zhí)行一次痒给,也就是說(shuō)對(duì)象自救一次后再次被標(biāo)記則無(wú)法通過(guò)finalize()方法自救。
  1. 垃圾收集算法
    虛擬機(jī)采用分代收集的機(jī)制骏全。一般是根據(jù)對(duì)象的存貨周期分為新生代和老年代苍柏。
  • 標(biāo)記-清除算法:顧名思義,先標(biāo)記后清除姜贡。將導(dǎo)致內(nèi)存的碎片化试吁。
  • 復(fù)制算法:在新生代區(qū)域大多數(shù)對(duì)象都是“朝生暮死”的前提下,將新生代堆分為Eden區(qū)和兩個(gè)較小的Survivor區(qū), 每次使用Eden區(qū)和其中一塊Survivor區(qū)楼咳,垃圾收集的時(shí)候?qū)⒋婊顚?duì)象復(fù)制到另外一塊Survivor區(qū)中熄捍,然后清理掉Eden和使用過(guò)的Survivor。
  • 標(biāo)記-整理算法:老年代的對(duì)象成活率較高爬橡,使用復(fù)制操作的話效率很低治唤。標(biāo)記整理算法在對(duì)對(duì)象進(jìn)行標(biāo)記之后不是直接清除棒动,而是將存活的對(duì)象向一端移動(dòng)糙申,最后清除掉邊界之外的內(nèi)存。
  1. 內(nèi)存分配和回收策略
  • 對(duì)象優(yōu)先在Eden區(qū)分配船惨。當(dāng)Eden區(qū)沒(méi)有足夠空間的時(shí)候柜裸,系統(tǒng)發(fā)起一次MinorGC(新生代回收)
  • 大對(duì)象直接分配在老年代。比如長(zhǎng)字符串和數(shù)組粱锐。-XX:PretenureSizeThreshold參數(shù)可以設(shè)置直接分配進(jìn)入老年代對(duì)象的大小分界線疙挺。
  • 長(zhǎng)期存活的對(duì)象將進(jìn)入老年代。每個(gè)對(duì)象都有一個(gè)age計(jì)數(shù)器怜浅,如果Eden對(duì)象經(jīng)過(guò)MinorGC沒(méi)有被收集铐然,根據(jù)復(fù)制算法將進(jìn)入Survivor區(qū),age為1恶座,此后該對(duì)象每次熬過(guò)MinorGC搀暑,age+=1。當(dāng)age增加到一定程度(默認(rèn)15)跨琳,將會(huì)晉升至老年代自点。該age參數(shù)可以通過(guò) -XX:MaxTenuringThreshold 進(jìn)行設(shè)置。
  • 動(dòng)態(tài)對(duì)象年齡判定:如果Survivor空間中相同年齡的對(duì)象大小總和大于了Survivor空間的一半脉让,那么年齡大于或等于該年齡的對(duì)象將直接進(jìn)入老年代桂敛。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末功炮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子术唬,更是在濱河造成了極大的恐慌薪伏,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粗仓,死亡現(xiàn)場(chǎng)離奇詭異毅该,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)潦牛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)眶掌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人巴碗,你說(shuō)我怎么就攤上這事朴爬。” “怎么了橡淆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵召噩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我逸爵,道長(zhǎng)具滴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任师倔,我火速辦了婚禮构韵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘趋艘。我一直安慰自己疲恢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布瓷胧。 她就那樣靜靜地躺著显拳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搓萧。 梳的紋絲不亂的頭發(fā)上杂数,一...
    開(kāi)封第一講書(shū)人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音瘸洛,去河邊找鬼揍移。 笑死,一個(gè)胖子當(dāng)著我的面吹牛货矮,可吹牛的內(nèi)容都是我干的羊精。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喧锦!你這毒婦竟也來(lái)了读规?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤燃少,失蹤者是張志新(化名)和其女友劉穎束亏,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體阵具,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碍遍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阳液。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怕敬。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帘皿,靈堂內(nèi)的尸體忽然破棺而出东跪,到底是詐尸還是另有隱情,我是刑警寧澤鹰溜,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布虽填,位于F島的核電站,受9級(jí)特大地震影響曹动,放射性物質(zhì)發(fā)生泄漏斋日。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一墓陈、第九天 我趴在偏房一處隱蔽的房頂上張望恶守。 院中可真熱鬧,春花似錦跛蛋、人聲如沸熬的。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至岔绸,卻和暖如春理逊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盒揉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工晋被, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刚盈。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓羡洛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親藕漱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欲侮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容