這篇文章記錄的是:深入理解java虛擬機(jī)(第二版)第三章與第四章的內(nèi)容
1. GC要做的三件事:
Q1: 哪些內(nèi)存需要回收蛾号?
GC回收的一般是堆(只用在程序運(yùn)行期間才能知道會(huì)創(chuàng)建哪些對(duì)象)和方法區(qū)(一個(gè)方法中的多個(gè)分支需要的內(nèi)存可能不一樣)中存放的已“死去的”java對(duì)象喊儡,那怎么判斷對(duì)象已經(jīng)“死去”了呢翎卓?1. 引用計(jì)數(shù)算法:給對(duì)象添加一個(gè)計(jì)數(shù)器诗充,每當(dāng)在一個(gè)地方引用它時(shí)镊掖,計(jì)數(shù)器就加1缎除,當(dāng)引用失效時(shí)膝但,計(jì)數(shù)器就減1环形,任何時(shí)候計(jì)數(shù)器為零的對(duì)象是不可能再被使用的策泣。問(wèn)題:很難解決對(duì)象之間相互循環(huán)引用的問(wèn)題。2. 可達(dá)性分析算法:以一系列被稱(chēng)為“GC Roots”的對(duì)象作為起始點(diǎn)抬吟,從這個(gè)節(jié)點(diǎn)開(kāi)始向下搜索萨咕,搜索所走過(guò)的路徑稱(chēng)為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何的引用鏈相連火本,則證明此對(duì)象不可用危队,即可回收。在Java中钙畔,可作為GC Roots的對(duì)象包括:虛擬機(jī)棧中引用的對(duì)象茫陆、方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象、方法區(qū)中常量引用的對(duì)象擎析、本地方法棧中JNI引用的對(duì)象
Q2:什么時(shí)候回收簿盅?
系統(tǒng)決定,無(wú)法預(yù)測(cè)
Q3:如何回收揍魂?
對(duì)于不同的內(nèi)存采用不同的收集算法桨醋。比如:Java一般把堆分為新生代和老年代,新生代(特點(diǎn):朝生夕死)一般采用復(fù)制算法现斋,老年代(特點(diǎn):存活率高)采用“標(biāo)記-清除”或者“標(biāo)記-整理”
2. 垃圾收集算法
2.1 標(biāo)記-清除算法
如同它的名字一樣喜最,算法分為“標(biāo)記”和“清除兩個(gè)階段”:首先標(biāo)記出所有要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象步责。不足:1. 標(biāo)記和清除兩個(gè)過(guò)程的效率都不高返顺。2. 標(biāo)記清除之后會(huì)產(chǎn)生許多大量不連續(xù)的內(nèi)存碎片禀苦,空間碎片太多可能導(dǎo)致以后在程序運(yùn)行過(guò)程中需要分配較大的對(duì)象時(shí),無(wú)法找到足夠連續(xù)的內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作遂鹊。
2.2 復(fù)制算法
將可用內(nèi)存按容量劃分為大小相等的兩塊振乏,每次只使用其中一塊,當(dāng)這一塊的內(nèi)存用完了秉扑,就將還存活著的對(duì)象復(fù)制到另一塊上面慧邮,然后把使用過(guò)的內(nèi)存空間一次清理掉。不足:內(nèi)存縮小為原來(lái)的一般舟陆。
根據(jù)IBM公司研究表明:98%的對(duì)象都是“朝生夕死”的误澳,所以?xún)?nèi)存劃分為一塊較大的Eden和兩塊較小的Survivor,比率:8:1:1秦躯,每次使用Eden和其中一塊Survivor忆谓。當(dāng)垃圾回收時(shí),將Eden和Survivor中還存活的對(duì)象一次性復(fù)制到另一塊Survivor空間上踱承,最后清理掉Eden和剛才用過(guò)的Survivor空間倡缠,當(dāng)然,我們不能保證每次回收時(shí)只有不多于10%的對(duì)象存活茎活,當(dāng)Survivor空間不夠用時(shí)昙沦,進(jìn)行分配擔(dān)保。
2.3 標(biāo)記-整理算法
復(fù)制算法在對(duì)象存活率較高的時(shí)候就要就行較多的復(fù)制操作载荔,效率將會(huì)變低盾饮。標(biāo)記-整理算法與“標(biāo)記-清除”算法一樣,但是后續(xù)步驟并不是直接對(duì)可回收的對(duì)象進(jìn)行清理懒熙,而是讓所有存活的對(duì)象都像一端移動(dòng)丘损,然后直接清理掉邊界以外的內(nèi)存。
2. 垃圾收集器
2.1 Serial 收集器
單線程的收集器煌珊。單線程的意義并不僅僅說(shuō)明它只會(huì)是有一個(gè)CPU或一條收集線程去完成垃圾收集的工作号俐,更重要的是在它進(jìn)行垃圾收集時(shí),必須暫停其他所有的工作線程(Stop The World)定庵,直到它收集結(jié)束吏饿。很明顯,“Stop The World” 使得用戶體驗(yàn)度不好蔬浙。
- 使用場(chǎng)景:是HotSpot在Client模式下默認(rèn)的新生代收集器
2.2 ParNew收集器
ParNew收集器是Serial收集器的多線程版本猪落,除了使用多線程進(jìn)行垃圾收集之外,其他的控制參數(shù)與Serial收集器幾乎完全一樣畴博。
- 使用場(chǎng)景:在Server模式下笨忌,ParNew收集器是一個(gè)非常重要的收集器,因?yàn)槌齋erial外俱病,目前只有它能與CMS收集器配合工作(因?yàn)镻arallel Scavenge(以及G1)都沒(méi)有使用傳統(tǒng)的GC收集器代碼框架官疲,而另外獨(dú)立實(shí)現(xiàn))袱结,但在單個(gè)CPU環(huán)境中,不會(huì)比Serail收集器有更好的效果途凫,因?yàn)榇嬖诰€程交互開(kāi)銷(xiāo)垢夹。
2.3 Parallel Scavenge 收集器
使用復(fù)制算法的收集器,也是并行的多線程收集器维费。特點(diǎn):達(dá)到一個(gè)可控制的吞吐量果元,所謂吞吐量就是CPU用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值,即吞吐量=運(yùn)行用戶代碼/(運(yùn)行用戶代碼+垃圾收集時(shí)間)犀盟。該收集器提供了兩個(gè)參數(shù)用于精確地控制吞吐量而晒,分別是控制垃圾收集停頓時(shí)間的-XX:MaxGCPauseMillis參數(shù)以及直接設(shè)置吞吐量大小的-XX:GCTimeRatio參數(shù)。不過(guò)阅畴,不要認(rèn)為把停頓時(shí)間設(shè)置小一點(diǎn)就能使系統(tǒng)的垃圾收集速度變得更快倡怎,GC停頓時(shí)間縮短是以犧牲吞吐量和新生代空間來(lái)?yè)Q取的:系統(tǒng)把新生代調(diào)小一些,收集300MB新生代肯定比收集500MB快吧贱枣,這也直接導(dǎo)致垃圾收集發(fā)生得更加頻繁诈胜,原來(lái)10秒收集一次,每次停頓10毫秒冯事,現(xiàn)在變成5秒收集一次、每次停頓70毫秒血公。停頓時(shí)間的確在下降昵仅,但吞吐量也降下來(lái)了。
-
使用場(chǎng)景:高吞吐量為目標(biāo)累魔,即減少垃圾收集時(shí)間摔笤,讓用戶代碼獲得更長(zhǎng)的運(yùn)行時(shí)間; 當(dāng)應(yīng)用程序運(yùn)行在具有多個(gè)CPU上垦写,對(duì)暫停時(shí)間沒(méi)有特別高的要求時(shí)吕世,即程序主要在后臺(tái)進(jìn)行計(jì)算,而不需要與用戶進(jìn)行太多交互梯投;
2.4 Serial Old 收集器
Serial Old是Serial收集器的老年代版本命辖,它是使用“標(biāo)記-整理”算法的單線程收集器。這個(gè)收集器的主要意義在于給Client模式下的虛擬機(jī)使用分蓖。如果在Server模式下尔艇,主要有兩大用途:1. JDK1.5之前的版本中與Parallel Scavenge收集器搭配使用。2. 作為CMS收集器的后備預(yù)案么鹤。
- 使用場(chǎng)景:主要用于Client模式终娃;Server模式:作為CMS收集器的后備預(yù)案
2.5 Parallel Old 收集器
Parallel Old 是Parallel Scavenge 收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法蒸甜。在JDK1.6之前棠耕,Parallel Scavenge收集器余佛,老年代除了Serial Old之外別無(wú)選擇,由于Serial Old 收集器在性能上的“拖累”窍荧,使得Parallel Scavenge 收集器未必能獲得吞吐量最大化的效果辉巡。知道Parallel Old 收集器出現(xiàn)之后,“吞吐量?jī)?yōu)先”才有了名副其實(shí)的組合搅荞。
- 使用場(chǎng)景:JDK1.6及之后红氯,在Server模式,多CPU的情況下用來(lái)代替老年代的Serial Old收集器咕痛;
2.5 CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器痢甘。采用“標(biāo)記-清除”算法,總共有4個(gè)步驟:
- 初始標(biāo)記
- 并發(fā)標(biāo)記
- 重新標(biāo)記
-
并發(fā)清除
初始標(biāo)記和重新標(biāo)記需要“Stop The Word”茉贡。初始標(biāo)記僅僅只是標(biāo)記GC Root能直接關(guān)聯(lián)的對(duì)象塞栅,速度很快,并發(fā)標(biāo)記階段進(jìn)行GC Roots Tracing過(guò)程腔丧,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那部分對(duì)象的標(biāo)記記錄放椰。優(yōu)點(diǎn):并發(fā)收集、低停頓愉粤。缺點(diǎn):1. CMS收集器對(duì)CPU資源非常敏感(占用CPU砾医,導(dǎo)致應(yīng)用程序變慢,總吞吐量降低)衣厘。2. CMS收集器無(wú)法處理浮動(dòng)垃圾(清理的同時(shí)如蚜,程序還在連續(xù)不斷的產(chǎn)生新的垃圾)。3. “標(biāo)記-清除”算法會(huì)產(chǎn)生大量的空間碎片影暴。
- 使用場(chǎng)景: 與用戶交互較多的場(chǎng)景错邦,希望系統(tǒng)停頓時(shí)間最短,注重服務(wù)的響應(yīng)速度型宙,以給用戶帶來(lái)較好的體驗(yàn)撬呢, 如常見(jiàn)WEB、B/S系統(tǒng)的服務(wù)器上的應(yīng)用
2.6 G1 收集器
G1(Garbage-First)收集器
G1收集器是當(dāng)今收集器技術(shù)的最前沿成果之一妆兑。用來(lái)替換JDK1.5中發(fā)布的CMS收集器魂拦。特點(diǎn):
- 并行與并發(fā):充分利用多CPU、多核環(huán)境下的硬件優(yōu)勢(shì)搁嗓。
- 分代收集:G1不需要配合其他的收集器就能獨(dú)立管理整個(gè)GC堆晨另,但它會(huì)采用不同的方式去處理新創(chuàng)建的對(duì)象和已經(jīng)存活了一段時(shí)間、熬過(guò)多次GC的舊對(duì)象谱姓。
- 空間整合:與CMS的“標(biāo)記-清除”算法不同借尿,G1是基于“標(biāo)記-整理”算法實(shí)現(xiàn)的,所以不會(huì)產(chǎn)生空間碎片。
- 可預(yù)測(cè)停頓:能指定在一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段內(nèi)路翻,消耗在垃圾收集上的時(shí)間不得超過(guò)N毫秒狈癞。
G1收集器大致分為以下幾步: - 初始標(biāo)記
- 并發(fā)標(biāo)記
- 最終標(biāo)記
-
篩選回收
可以看出來(lái),過(guò)程與CMS差不多茂契,最終標(biāo)記階段會(huì)把Remembered Set Logs的數(shù)據(jù)合并到Remembered Set中蝶桶。最后在篩選回收階段首先對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶所期望的GC停頓時(shí)間來(lái)制定回收計(jì)劃
-
使用場(chǎng)景: 面向服務(wù)端應(yīng)用掉冶,針對(duì)具有大內(nèi)存真竖、多處理器的機(jī)器,最主要的應(yīng)用是為需要低GC延遲厌小,并具有大堆的應(yīng)用程序提供解決方案恢共;