一使鹅、垃圾收集算法
1.標(biāo)記-清除算法
最基礎(chǔ)的收集算法是“標(biāo)記-清除”(Mark-Sweep)算法揪阶,如同它的名字一樣,算法分為“標(biāo)記”和“清除”兩個階段患朱。
①首先標(biāo)記出所有需要回收的對象
②在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象鲁僚。
不足:
效率問題:標(biāo)記和清除兩個過程的效率都不高
空間問題:標(biāo)記清除之后產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后程序運(yùn)行過程中需要分配較大對象時裁厅,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作冰沙。
2.復(fù)制算法
目的: 為了解決效率問題。
將可用內(nèi)存按容量大小劃分為大小相等的兩塊执虹,每次只使用其中的一塊拓挥。當(dāng)一塊內(nèi)存使用完了,就將還存活著的對象復(fù)制到另一塊上面袋励,然后再把已使用過的內(nèi)存空間一次清理掉侥啤。這樣使得每次都是對整個半?yún)^(qū)進(jìn)行內(nèi)存回收当叭,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復(fù)雜情況。
缺點(diǎn): 將內(nèi)存縮小為了原來的一半盖灸。
現(xiàn)代的商業(yè)虛擬機(jī)都采用這種收集算法來回收新生代蚁鳖,IBM公司的專門研究表明,新生代中對象98%對象是“朝生夕死”的赁炎,所以不需要按照1:1的比例來劃分內(nèi)存空間醉箕,而是將內(nèi)存分為較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor徙垫。HotSpot虛擬機(jī)中默認(rèn)Eden和Survivor的大小比例是8:1讥裤。
3.標(biāo)記-整理算法
復(fù)制收集算法在對象存活率較高時,就要進(jìn)行較多的復(fù)制操作松邪,效率就會變低坞琴。 根據(jù)老年代的特點(diǎn),提出了“標(biāo)記-整理”算法逗抑。
標(biāo)記過程仍然與”標(biāo)記-清除“算法一樣剧辐,但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動邮府,然后直接清理掉邊界以外的內(nèi)存荧关。
4.分代收集算法
一般是把Java堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
在新生代中褂傀,每次垃圾收集時都發(fā)現(xiàn)有大批對象死去忍啤,只有少量存活,那就選用復(fù)制算法仙辟。
在老年代中同波,因?yàn)閷ο蟠婊盥矢摺]有額外空間對它進(jìn)行分配擔(dān)保叠国,就必須采用“標(biāo)記-清除”或“標(biāo)記-整理”算法來進(jìn)行回收未檩。
二、垃圾回收機(jī)制的一些知識
1.JVM中的年代
JVM中分為年輕代(Young generation)和老年代(Tenured generation)粟焊。
HotSpot JVM把年輕代分為了三部分:1個Eden區(qū)和2個Survivor區(qū)(分別叫from和to)冤狡。默認(rèn)比例為8:1,為啥默認(rèn)會是這個比例项棠,接下來我們會聊到悲雳。
一般情況下,新創(chuàng)建的對象都會被分配到Eden區(qū)(一些大對象特殊處理)香追,這些對象經(jīng)過第一次Minor GC后合瓢,如果仍然存活,將會被移到Survivor區(qū)透典。對象在Survivor區(qū)中每熬過一次Minor GC晴楔,年齡就會增加1歲迁央,當(dāng)它的年齡增加到一定程度時,就會被移動到年老代中滥崩。 因?yàn)槟贻p代中的對象基本都是朝生夕死的(80%以上),所以在年輕代的垃圾回收算法使用的是復(fù)制算法讹语,復(fù)制算法的基本思想就是將內(nèi)存分為兩塊钙皮,每次只用其中一塊,當(dāng)這一塊內(nèi)存用完顽决,就將還活著的對象復(fù)制到另外一塊上面短条。復(fù)制算法不會產(chǎn)生內(nèi)存碎片。
在GC開始的時候才菠,對象只會存在于Eden區(qū)和名為“From”的Survivor區(qū)茸时,Survivor區(qū)“To”是空的。緊接著進(jìn)行GC赋访,Eden區(qū)中所有存活的對象都會被復(fù)制到“To”可都,而在“From”區(qū)中,仍存活的對象會根據(jù)他們的年齡值來決定去向蚓耽。年齡達(dá)到一定值(年齡閾值渠牲,可以通過-XX:MaxTenuringThreshold來設(shè)置)的對象會被移動到年老代中,沒有達(dá)到閾值的對象會被復(fù)制到“To”區(qū)域步悠。經(jīng)過這次GC后签杈,Eden區(qū)和From區(qū)已經(jīng)被清空。這個時候鼎兽,“From”和“To”會交換他們的角色答姥,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”谚咬。不管怎樣鹦付,都會保證名為To的Survivor區(qū)域是空的。Minor GC會一直重復(fù)這樣的過程序宦,直到“To”區(qū)被填滿睁壁,“To”區(qū)被填滿之后,會將所有對象移動到年老代中互捌。
2.Minor GC和Full GC的區(qū)別
Minor GC:指發(fā)生在新生代的垃圾收集動作潘明,該動作非常頻繁。
Full GC/Major GC:指發(fā)生在老年代的垃圾收集動作秕噪,出現(xiàn)了Major GC钳降,經(jīng)常會伴隨至少一次的Minor GC。Major GC的速度一般會比Minor GC慢10倍以上腌巾。
3. 空間分配擔(dān)保
在發(fā)生Minor GC之前遂填,虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象的總空間铲觉,如果這個條件成立,那么Minor GC可以 確保是安全的吓坚。如果不成立撵幽,則虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗。如果允許礁击,那會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小盐杂,如果大于,則將嘗試進(jìn)行一次Minor GC哆窿,盡管這個Minor GC是有風(fēng)險的链烈。如果小于,或者HandlePromotionFailure設(shè)置不允許冒險挚躯,那這時也要改為進(jìn)行一次Full GC强衡。
以上便是在垃圾回收過程中,需要了解的一些必要的知識码荔。下面我們就來介紹具體的垃圾收集器漩勤。
三、垃圾收集器
上圖展示了7種作用于不同分代的收集器目胡,如果兩個收集器之間存在連線锯七,說明它們可以搭配使用。
1.Serial收集器
是最基本誉己、發(fā)展歷史最悠久的收集器眉尸。這是一個單線程收集器。但它的“單線程”的意義并不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作巨双,更重要的是它在進(jìn)行垃圾收集時噪猾,必須暫停其他所有的工作線程,直到它收集結(jié)束筑累。
是虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)新生代收集器袱蜡。
優(yōu)勢: 簡單而高效(與其他收集器的單線程比),對于限定單個CPU的環(huán)境來說慢宗,Serial收集器由于沒有線程交互的開銷坪蚁,專心做垃圾收集自然可以獲得最高的單線程效率。
2.ParNew收集器
ParNew收集器其實(shí)就是Serial收集器的多線程版本镜沽。
是許多運(yùn)行在Server模式下的虛擬機(jī)中首選的新生代收集器敏晤,其中一個與性能無關(guān)但很重要的原因是,除了Serial收集器外缅茉,目前只有它能與CMS收集器配合工作嘴脾。
ParNew收集器默認(rèn)開啟的收集線程數(shù)與CPU的數(shù)量相同。 下圖是ParNew/Serial Old收集器運(yùn)行示意圖
3.Parallel Scavenge收集器
Parallel Scavenge收集器是一個新生代收集器,使用復(fù)制算法译打,又是并行的多線程收集器耗拓。
最大的特點(diǎn)是: Parallel Scavenge收集器的目標(biāo)是達(dá)到一個可控制的吞吐量。
所謂吞吐量就是CPU用于運(yùn)行用戶代碼的時間與CPU總消耗時間的比值奏司,即吞吐量=運(yùn)行用戶代碼時間/(運(yùn)行用戶代碼時間+垃圾收集時間)乔询。
高吞吐量則可以高效率地利用CPU時間,盡快完成程序的運(yùn)算任務(wù)韵洋,主要適合在后臺運(yùn)算而不需要太多交互的任務(wù)哥谷。
4.Serial Old收集器
Serial Old是Serial收集器的老年代版本,同樣是一個單線程收集器麻献,使用“標(biāo)記-整理”算法。這個收集器的主要意義也是在于給Client模式下虛擬機(jī)使用猜扮。
如果在Server模式下勉吻,它主要還有兩大用途:
1.與Parallel Scavenge收集器搭配使用
2.作為CMS收集器的后備預(yù)案,在并發(fā)收集發(fā)生Conurrent Mode Failure使用旅赢。
5.Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本齿桃,使用多線程和“標(biāo)記-整理”算法。
在注重吞吐量以及CPU資源敏感的場合煮盼,都可以優(yōu)先考慮Parallel Scavenge+Parallel Old收集器
6.CMS(Concurrent Mark Sweep)收集器
是HotSpot虛擬機(jī)中第一款真正意義上的并發(fā)收集器短纵,它第一次實(shí)現(xiàn)了讓垃圾收集線程與用戶線程同時工作。
關(guān)注點(diǎn): 盡可能地縮短垃圾收集時用戶線程的停頓時間僵控。
CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的香到,整個過程分為4個步驟:
①初始標(biāo)記
②并發(fā)標(biāo)記
③重新標(biāo)記
④并發(fā)清除
其中,初始標(biāo)記报破,重新標(biāo)記這兩個步驟仍然需要“Stop The World”悠就。初始標(biāo)記僅僅只標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象,速度很快充易。并發(fā)標(biāo)記階段就是 進(jìn)行GC Roots Tracing的過程梗脾。
重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記幾率,這個階段的停頓時間一般會比初始標(biāo)記階段稍長盹靴,但遠(yuǎn)比并發(fā)標(biāo)記時間短炸茧。
整個過程耗時最長的階段是并發(fā)標(biāo)記,并發(fā)清除過程稿静,但這兩個過程可以和用戶線程一起工作梭冠。
缺點(diǎn):
①CMS收集器對CPU資源非常敏感。在并發(fā)階段自赔,它雖然不會導(dǎo)致用戶線程停頓妈嘹,但是會因?yàn)檎加昧艘徊糠志€程(或者說CPU資源)而導(dǎo)致應(yīng)用程序變慢,總吞吐量會降低绍妨。
②CMS收集器無法處理浮動垃圾润脸,可能出現(xiàn)“Conurrent Mode Failure”失敗而導(dǎo)致另一次Full GC的產(chǎn)生柬脸。由于CMS并發(fā)清理階段用戶線程還在運(yùn)行著,伴隨程序運(yùn)行自然就還會產(chǎn)生新的垃圾毙驯,這一部分垃圾出現(xiàn)在標(biāo)記過程之后倒堕,CMS無法在檔次收集中處理掉它們,只好留待下一次GC時再清理掉爆价。這部分垃圾就稱為“浮動垃圾”垦巴。因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進(jìn)行收集,需要預(yù)留一部分空間提供并發(fā)收集時程序運(yùn)作使用铭段。在JDK1.5的默認(rèn)設(shè)置下骤宣,CMS收集器當(dāng)老年代使用了68%的空間后就會被激活。如果預(yù)留空間無法滿足程序需要序愚,就會出現(xiàn)一次“Concurrent Mode Failure”失敗憔披,這時虛擬機(jī)將啟動后備預(yù)案Serial Old。
③CMS是一款基于“標(biāo)記-清除”算法實(shí)現(xiàn)的收集器爸吮,所以會有大量空間碎片問題芬膝。
7.G1收集器
是當(dāng)今收集器技術(shù)發(fā)展的最前沿成果之一。是一款面向服務(wù)端應(yīng)用的垃圾收集器形娇。
特點(diǎn):
①并行與并發(fā)
能充分利用多CPU锰霜,多核環(huán)境下的硬件優(yōu)勢,縮短Stop-The-World停頓的時間桐早,同時可以通過并發(fā)的方式讓Java程序繼續(xù)執(zhí)行
②分代收集
可以不需要其他收集器的配合管理整個堆癣缅,但是仍采用不同的方式去處理分代的對象。
③空間整合
G1從整體上來看哄酝,采用基于“標(biāo)記-整理”算法實(shí)現(xiàn)收集器
G1從局部上來看所灸,采用基于“復(fù)制”算法實(shí)現(xiàn)。
④可預(yù)測停頓
使用G1收集器時炫七,Java堆內(nèi)存布局與其他收集器有很大差別爬立,它將整個Java堆劃分成為多個大小相等的獨(dú)立區(qū)域。 G1跟蹤各個Region里面的垃圾堆積的價值大型蚰摹(回收所獲得的空間大小以及回收所需時間的經(jīng)驗(yàn)值)侠驯,在后臺維護(hù)一個優(yōu)先列表,每次根據(jù)允許的收集時間奕巍,優(yōu)先回收價值最大的Region吟策。