引言
在內(nèi)存管理中,垃圾是指那些不再被使用的對(duì)象。對(duì)于一個(gè)垃圾回收器(Garbage Collector)髓抑,它需要完成三件事:
- 分配內(nèi)存:垃圾回收算法的設(shè)計(jì)往往制約了內(nèi)存分配的方式飞蚓;
- 確保存活對(duì)象不會(huì)被回收
- 回收垃圾對(duì)象
很顯然的是,一個(gè)垃圾回收器并不提供回收全部垃圾對(duì)象的保證曲饱。這意味著一次垃圾回收結(jié)束之后,內(nèi)存中依然可能存活著不會(huì)再被使用的對(duì)象珠月。
對(duì)于HotSpot中的垃圾回收算法而言扩淀,其設(shè)計(jì)都是建立在兩個(gè)經(jīng)驗(yàn)法則之上的:
- 對(duì)于大部分對(duì)象來(lái)說(shuō),它會(huì)在“年輕”的時(shí)候死去啤挎;
- 很少引用存在于“年老的”和“年輕的”對(duì)象之間驻谆;
這里使用的“年輕的”和“年老的”的兩種說(shuō)法,是指存活時(shí)間的長(zhǎng)短庆聘。這兩條的經(jīng)驗(yàn)法則表明胜臊,大部分的對(duì)象存活時(shí)間都很短,無(wú)法活過(guò)一個(gè)回收周期伙判。并且象对,存活時(shí)間長(zhǎng)的對(duì)象傾向于引用存活時(shí)間長(zhǎng)的對(duì)象,而存活時(shí)間短的對(duì)象傾向于引用存活時(shí)間短的對(duì)象宴抚。對(duì)于大部分的應(yīng)用來(lái)說(shuō)勒魔,這兩個(gè)經(jīng)驗(yàn)法則都是適用的。
設(shè)計(jì)因素
在垃圾算法回收的設(shè)計(jì)中菇曲,需要考慮的因素有:
- 串行(Serial)和并行(Parallel)
- 并發(fā)(Concurrent)和暫停式(Stop the world)
- 壓縮(Compacting)和非壓縮(Non-Compacting):這一組概念是指冠绢,在垃圾回收結(jié)束之后,是否需要把所有的存活對(duì)象挪到一起常潮,占據(jù)一個(gè)連續(xù)空間弟胀。在Compacting之后,也意味著可用內(nèi)存占據(jù)了一個(gè)連續(xù)的空間,這個(gè)時(shí)候就可以使用bump-the-pointer的分配內(nèi)存技術(shù)邮利。在這種技術(shù)中弥雹,只需要持有一個(gè)指針指向已分配內(nèi)存的尾部。每次分配的時(shí)候只需要檢查剩余空閑空間能否容納新的對(duì)象延届,而后分配內(nèi)存并且將指針指向新的尾部剪勿。這種Compacting的計(jì)數(shù),在一些算法中需要付出額外的代價(jià)方庭,這個(gè)代價(jià)要么是需要額外的內(nèi)存空間厕吉,要么是額外的回收時(shí)間。
并發(fā)和并行是一對(duì)比較容易搞混的概念械念。并發(fā)是指头朱,垃圾回收和應(yīng)用可以在一段時(shí)間內(nèi)同時(shí)運(yùn)行,這個(gè)概念和操作系統(tǒng)上的概念是一致的龄减。在HotSpot中项钮,并發(fā)垃圾回收算法中大部分垃圾回收的工作都是在并發(fā)的情況下完成的,但是并不能完全免除Stop-the-world希停。并行是指利用多個(gè)CPU烁巫,多個(gè)線程同時(shí)進(jìn)行垃圾回收。
度量
在衡量一個(gè)垃圾回收算法上宠能,最為主要的兩個(gè)度量是:
- 暫停時(shí)間(pause time):是指在一次垃圾回收中亚隙,Stop-the-world狀態(tài)下占用的時(shí)間。暫停時(shí)間主要受到算法和堆大小的影響违崇。相同條件下藻糖,堆越小潜索,暫停時(shí)間就越短题诵。但是堆越小梢睛,那么回收頻率就越高。
- 吞吐量(throughput):一般而言肴楷,堆越大水由,吞吐量越高荠呐,回收頻率越低赛蔫。
可以看到一個(gè)有趣的地方:暫停時(shí)間和吞吐量對(duì)堆的大小要求是不一樣的。暫停時(shí)間要想短泥张,那么應(yīng)該有更小堆呵恢;而吞吐量要大,需要更大的堆媚创。
分代
前面提到渗钉,大部分的對(duì)象都會(huì)在年輕的時(shí)候死去。因此將那些年輕對(duì)象放在一起,將那些年老的對(duì)象放在一起鳄橘,在垃圾回收的時(shí)候區(qū)別對(duì)待声离,就是很有價(jià)值的。這就是分代(generation)的想法瘫怜。在HotSpot中术徊,所有的垃圾回收算法都是分代垃圾回收算法。年輕代垃圾回收更加頻繁鲸湃,并且在一次回收中能夠存活下來(lái)的對(duì)象也是及其稀少的赠涮。在年輕代存活過(guò)一段時(shí)間(通常是活過(guò)了幾次年輕代的垃圾回收周期)后,對(duì)象會(huì)被提升(promoted or tenured)到老年代暗挑。如圖:
注:圖片引自Memory Managerment in the Java HotSpot Virtual Machine
除此以外笋除,大對(duì)象也會(huì)直接分配在老年代≌桑總體來(lái)說(shuō)垃它,老年代的對(duì)象存活時(shí)間更長(zhǎng),并且老年代分配出去的空間增長(zhǎng)緩慢烹看,由此導(dǎo)致了老年代垃圾回收不頻繁嗤瞎。
這種提升策略會(huì)有一個(gè)問(wèn)題,就是老年代可能并沒(méi)有足夠的空間容納這些從年輕代提升上來(lái)的對(duì)象听系。在這種情況下贝奇,會(huì)觸發(fā)一次老年代的垃圾回收。在極端的情況下靠胜,整個(gè)年輕代的所有對(duì)象都會(huì)被提升到老年代掉瞳。因此,老年代比年輕代要大是順理成章的事情浪漠。
關(guān)于老年代什么時(shí)候檢測(cè)剩余空閑空間陕习,判斷能否容納被提升的對(duì)象,我閱讀的文檔文章中并沒(méi)有明確提出址愿。它可能發(fā)生在每提升一個(gè)對(duì)象该镣,就執(zhí)行一次檢測(cè);也可能是在年輕代回收之前响谓,根據(jù)有多少待提升對(duì)象進(jìn)行一次檢測(cè)损合;最后一種可能是,檢測(cè)發(fā)生在年輕代垃圾回收之前——也就是意味著每次檢測(cè)都是拿年輕代的大小和老年代空閑空間的大小進(jìn)行比較娘纷。在Tuning Garbaga Collection with the 5.0 Java Virtual Machine中有一段話嫁审,“But for applications needing the largest possible heap, an eden bigger than half the virtually committed size of the heap is useless: only major collections would occur”。從這句話來(lái)看赖晶,老年代似乎并不管究竟有多少需要提升律适,也不管會(huì)回收多少垃圾,每一次比較都是用空閑空間和整個(gè)年輕代的大小進(jìn)行比較。
在HotSpot中的分代為:
- 年輕代(young generation)分成Eden和Survivor捂贿。(注:在一些特殊的平臺(tái)上纠修,HotSpot并沒(méi)有Eden和Survivor的概念)
- Eden:年輕代只有一個(gè)Eden。對(duì)象分配都是直接分配在Eden中的厂僧;
- Survivor:年輕代中會(huì)有兩個(gè)Survivor分瘾,拿來(lái)存放在一次垃圾回收算法中存活的對(duì)象。每次算法運(yùn)行的時(shí)候吁系,會(huì)把Eden和一個(gè)Survivor(標(biāo)記為from)中的存活對(duì)象復(fù)制到另外一個(gè)Survivor(標(biāo)記為to)中德召;
- 老年代(old generation):
- 持久代(perm generation):持久代幾乎不會(huì)出現(xiàn)垃圾回收(有些應(yīng)用或者有些平臺(tái)會(huì)有這種需求),本文將不會(huì)涉及這個(gè)地方汽纤;
在HotSpot中上岗,Eden和Survivor的比例可以通過(guò)使用++NewRatio來(lái)
Serial Collector
原理
Serial Collector(串行回收器)在年輕代是一個(gè)使用標(biāo)記-復(fù)制算法的回收器。標(biāo)記-復(fù)制垃圾回收算法可以分成兩個(gè)階段:
- 標(biāo)記階段:從根(root)出發(fā)蕴坪,沿著引用鏈標(biāo)記存活對(duì)象肴掷;
- 復(fù)制:將存活對(duì)象復(fù)制到特定的區(qū)域;
如圖:
GC完成之后:
注:圖片引自Memory Managerment in the Java HotSpot Virtual Machine
在標(biāo)記階段背传,Serial Collector將Eden中和這次被標(biāo)記為from(恰好是上次標(biāo)記為to)的Survivor中的存活對(duì)象標(biāo)記出來(lái)呆瞻,而后將存活對(duì)象復(fù)制到另外一個(gè)Survivor中。在這個(gè)過(guò)程中径玖,可能有部分對(duì)象的存活時(shí)間達(dá)到了提升到老年代的標(biāo)準(zhǔn)痴脾,因此會(huì)有一些對(duì)象被復(fù)制到老年代。
Serial Collector也可以被應(yīng)用在老年代梳星,所不同的是赞赖,老年代并沒(méi)有什么Eden和Survivor的劃分。在老年代使用的是標(biāo)記-清掃-整理算法冤灾。該算法流程為:
- 標(biāo)記:比較存活的對(duì)象
- 清掃:在該階段前域,會(huì)識(shí)別出垃圾對(duì)象≡隙郑“清掃”這個(gè)概念帶有一些誤導(dǎo)的色彩匿垄,在算法的實(shí)現(xiàn)中,并不需要真的對(duì)垃圾對(duì)象占據(jù)的內(nèi)存進(jìn)行清掃归粉,僅僅標(biāo)記一下就夠了椿疗。(注:在新對(duì)象被分配到該被清掃的區(qū)域的時(shí)候,會(huì)執(zhí)行一次JVM層面上的初始化過(guò)程盏浇,該過(guò)程會(huì)把該內(nèi)存區(qū)域重置变丧,因而有了Java語(yǔ)言中的不同數(shù)據(jù)類型的默認(rèn)值一說(shuō))
- 整理:將所有的存活對(duì)象都挪到一端
可以看到的是,在使用Serial Collector的情況下绢掰,無(wú)論是老年代還是年輕代,其內(nèi)存都經(jīng)過(guò)了Compacting,所有的存活對(duì)象占據(jù)了一塊連續(xù)的空間滴劲,而空閑空間也是占據(jù)了一個(gè)連續(xù)的空間攻晒。因此在分配內(nèi)存空間的時(shí)候都可以使用bump-the-pointer的技術(shù)。
使用場(chǎng)景
Serial Collector主要適用于對(duì)pause time要求不高班挖,可用內(nèi)存和CPU數(shù)量都比較小的應(yīng)用上鲁捏。在新的HotSpot中,如果虛擬機(jī)運(yùn)行的平臺(tái)是client-style類型的萧芙,那么就會(huì)采用Serial Collector给梅。
影響因素和JVM選項(xiàng)
Serial Collector的性能和幾個(gè)因素有關(guān):
- 堆大小。如前面所言双揪,堆的大小直接影響了吞吐量和停頓時(shí)間(pause time)
- NewRatio:這個(gè)因素調(diào)節(jié)的是年輕代和老年代的比例动羽。比如說(shuō)值是3,那么意味著年輕代和老年代的比例是1:3渔期。對(duì)于Serial Collector來(lái)說(shuō)运吓,年輕代過(guò)大,會(huì)造成相應(yīng)老年代過(guò)小疯趟,這會(huì)導(dǎo)致更加頻繁地觸發(fā)老年代的垃圾回收(也即是major GC)拘哨。而如果年輕代過(guò)小,那么年輕代就會(huì)更加頻繁GC信峻,并且相對(duì)而言倦青,更加多的大對(duì)象會(huì)被直接分配到老年代。在這種情況下盹舞,觸發(fā)老年代GC的頻率要降低姨夹,但是pause time就要提升;
- SurvivorRatio:該選項(xiàng)調(diào)節(jié)的是年輕代中Suvivor部分占據(jù)的大小矾策。舉例來(lái)說(shuō)磷账,如果值是8,那么意味著贾虽,每個(gè)Survivor占據(jù)的大小是1/(8+2)逃糟。Survivor的大小是比較關(guān)鍵的。如果Survivor比較小蓬豁,那么Survivor很容易就裝不下存活對(duì)象绰咽,因此有更加多的存活對(duì)象被逼提升到老年代。這會(huì)帶來(lái)兩個(gè)影響:一個(gè)是老年代更加快裝滿地粪,另外一個(gè)是老年代指向年輕代的引用會(huì)增加——這個(gè)因素會(huì)影響mark階段根的大小取募。而如果Survivor過(guò)大,那么每次裝完存活對(duì)象之后還會(huì)剩下一段沒(méi)有利用的空間蟆技,這段空間就會(huì)被浪費(fèi)玩敏,影響年輕代的GC頻率和吞吐量斗忌;
還有其余的一些選項(xiàng),如NewSize和MaxNewSize等旺聚,讀者可以自己去找相關(guān)的資料閱讀织阳。
Parallel Collector
原理
Parallel Collector(并行垃圾回收器)和Serial Collector(串行回收器)比起來(lái),要復(fù)雜微妙很多砰粹∵蠖悖總體的算法思想是一致的,不過(guò)Serial Collector在任何階段都是單線程在運(yùn)行的碱璃,而Parallel Collector則是多線程運(yùn)行的弄痹。額外要注意的是,即便在虛擬機(jī)中指定了Parallel Collector嵌器,但是老年代的回收肛真,也就是major collection還是使用和Serail Collector一樣的單線程!換句話來(lái)說(shuō)嘴秸,只有對(duì)年輕代的回收(minor collection)將會(huì)采用多線程毁欣。如圖:
注:圖片來(lái)自Memory Managerment in the Java HotSpot Virtual Machine。
Parallel Collector與Serial Collector比起來(lái)岳掐,以下這些方面是要注意的:
- 工作分配凭疮。這是一個(gè)算法設(shè)計(jì)的難點(diǎn)。舉例來(lái)說(shuō)串述,我們可以將整個(gè)標(biāo)記階段看成是一個(gè)大的任務(wù)执解,由一系列的小的工作組成,那么在多線程的情況下纲酗,要考慮哪個(gè)線程負(fù)責(zé)標(biāo)記哪一塊衰腌,還要考慮負(fù)載均衡——即任務(wù)的分配要公平,不能一些線程很快完成任務(wù)停下來(lái)觅赊,等待其余線程完成自己的工作右蕊;
在標(biāo)記階段,還有一個(gè)很有意思的話題吮螺。就是如果多線程在進(jìn)行標(biāo)記的時(shí)候饶囚,可能會(huì)重復(fù)標(biāo)記一個(gè)對(duì)象為存活對(duì)象,這并不會(huì)影響算法的正確性鸠补,只是會(huì)影響算法的性能萝风。關(guān)于并行回收算法的更加細(xì)致的描述可以閱讀《垃圾回收算法手冊(cè)——自動(dòng)內(nèi)存管理的藝術(shù)》第14章。
- 在多線程的情況下紫岩,提升對(duì)象到老年代會(huì)遇到新的問(wèn)題规惰。按照原來(lái)的bump-the-point的算法,每一次分配都是指針的遞增泉蝌。假設(shè)說(shuō)現(xiàn)在有兩個(gè)線程同時(shí)在老年代分配空間歇万,分配前的指針指向100揩晴,其中一個(gè)線程將指針增加到150,而另外一個(gè)線程將指針設(shè)置為200.那么最終的結(jié)果就可能是150堕花,也可能是200文狱。一種自然的想法是加鎖粥鞋,但是和一般應(yīng)用里面鎖爭(zhēng)用會(huì)帶來(lái)?yè)p耗一般缘挽,這也會(huì)導(dǎo)致老年代的分配損耗增加(這會(huì)極大影響pause time和吞吐量)。而Paraller Collector采用了另外一種被稱為T(mén)LABs(thread-local allocation buffers)技術(shù):它將老年代劃分成一個(gè)個(gè)固定大小的Buffer呻粹,給每一個(gè)回收線程分配一個(gè)壕曼,回收線程就只在自己的Buffer內(nèi)分配空間。在這種情況下等浊,只有線程重新申請(qǐng)一個(gè)Buffer的時(shí)候腮郊,才會(huì)引入并發(fā)的問(wèn)題。但是這會(huì)帶來(lái)另外一個(gè)問(wèn)題筹燕,就是每一個(gè)線程并不能恰好用完一個(gè)Buffer轧飞,可能出現(xiàn)的情況是,一個(gè)線程檢測(cè)到Buffer里面的空閑空間已經(jīng)不夠了撒踪,于是只能申請(qǐng)另外一個(gè)Buffer过咬。而原來(lái)的Buffer的那部分不足的空間就會(huì)被浪費(fèi)掉。這就是所謂的float garbage制妄。
還有一個(gè)誤解要澄清掸绞。應(yīng)用有多少個(gè)線程在運(yùn)行并不決定有多少個(gè)線程回收。舉例說(shuō)一個(gè)應(yīng)用有200個(gè)線程正在運(yùn)行耕捞,但是在垃圾回收的時(shí)候衔掸,回收的線程可能只有10個(gè)。
Parallel Collector有一個(gè)變種叫做Parallel Compacting Collector俺抽。從名字上很令人困惑敞映,因?yàn)閺那懊鎸?duì)Serial Collector上的敘述中可以看出來(lái),major collection使用的算法磷斧,本身就是compacting的振愿。我簡(jiǎn)單描述一下兩者的區(qū)別:對(duì)于Parallel Collector來(lái)說(shuō),每一次回收都會(huì)Compacting整個(gè)老年代瞳抓;而對(duì)于Parallel Compacting Collector來(lái)說(shuō)并不是埃疫。它會(huì)檢測(cè)一個(gè)區(qū)域的存活對(duì)象的密度,來(lái)決定是否進(jìn)行compacting孩哑。更多相關(guān)信息可以閱讀《Memory Managerment in the Java HotSpot Virtual Machine》和《垃圾回收算法手冊(cè)——自動(dòng)內(nèi)存管理的藝術(shù)》栓霜。
使用場(chǎng)景
Parallel Collector又被叫做Throughput Collector。所以很顯然横蜒,它適用于要求吞吐量高的場(chǎng)景胳蛮。
影響因素和JVM選項(xiàng)
前面在Serial Collector中談及的影響因素销凑,對(duì)Parallel Collector同樣是適用的,而且還額外收到以下這些因素的影響:
- GC線程數(shù)量:線程數(shù)量和float garbage的數(shù)量是成正相關(guān)關(guān)系仅炊;
- Buffer大小
- CPU數(shù)量:在單個(gè)CPU的情況下斗幼,Parallel Collector的表現(xiàn)不如Serial Collector;在兩個(gè)CPU的情況下抚垄,Parallel Collector可以達(dá)到和Serial Collector相當(dāng)?shù)谋憩F(xiàn)蜕窿,甚至?xí)^(guò)一點(diǎn)。在CPU超過(guò)兩個(gè)的情況下呆馁, 表現(xiàn)都會(huì)比Serial Collector好桐经。但是并不是CPU越多性能越好。在當(dāng)前算法的實(shí)現(xiàn)中浙滤,CPU數(shù)量超過(guò)32(16阴挣?有點(diǎn)忘記了,閱讀的資料也忘了是哪個(gè)了)性能反而會(huì)下降纺腊;
CMS Collector
原理
CMS Collector(Concurrent Mark-Sweep Collector)畔咧,是一個(gè)并發(fā)垃圾回收器,這意味著在垃圾回收的過(guò)程中間揖膜,大部分時(shí)間里誓沸,應(yīng)用還是可以繼續(xù)運(yùn)行的。所以次氨,這里所謂的并發(fā)蔽介,更加多的是指應(yīng)用和垃圾回收的并發(fā)。同時(shí)CMS Collector也是多線程的煮寡,也即它也是Parallel(并行)的虹蓄。
在CMS Collector里,年輕代的回收是和Parallel Collector一樣的幸撕,也就是說(shuō)薇组,年輕代的回收是stop-the-world式的。只有在老年代坐儿,相應(yīng)的major collection里面才會(huì)使用CMS算法律胀。
CMS的過(guò)程如圖:
注:圖片來(lái)自Memory Managerment in the Java HotSpot Virtual Machine
- initial mark:initial mark是stop-the-world式的,也就是說(shuō)在這個(gè)階段是需要暫停應(yīng)用的執(zhí)行貌矿。initial mark只是識(shí)別出來(lái)標(biāo)記的根(準(zhǔn)確說(shuō)法是"identifies the initial set of live objects directly reachable from application code"炭菌,另外在StackOverFlow上看到一段話"This includes: Reference from thread stacks, Reference from young space.")
- concurrent mark:并發(fā)標(biāo)記階段。在該階段逛漫,應(yīng)用可以繼續(xù)運(yùn)行黑低;
- remark:在concurrent mark階段,因?yàn)閼?yīng)用依舊在運(yùn)行,所以可能原本標(biāo)記為垃圾的對(duì)象又“復(fù)活”了克握,也可能分配了新的對(duì)象蕾管。所以會(huì)引入找一個(gè)remark階段。該階段也是stop-the-world的菩暗;
- concurrent sweep:并發(fā)清掃掰曾。該階段應(yīng)用會(huì)繼續(xù)運(yùn)行;
因?yàn)橐雰蓚€(gè)并發(fā)階段停团,所以會(huì)造成很多問(wèn)題:
- 如何快速的remark旷坦?顯然,如果要是在remark中還是需要掃描對(duì)象客蹋,那么該Collector就沒(méi)多大必要存在了塞蹭。CMS Collector采用了一種稱為“card table”的技術(shù)孽江。card table簡(jiǎn)單理解是并發(fā)回收器的工作列表讶坯。CMS使用該技術(shù)會(huì)在concurrent mark階段,將改變了引用關(guān)系的對(duì)象標(biāo)記為“dirty”岗屏,在remark階段中重新掃描辆琅;
- 還有一個(gè)問(wèn)題是,在concurrent mark階段这刷,可能觸發(fā)minor collection婉烟。CMS Collector會(huì)采用一種稱為Mod Union Table的技術(shù)來(lái)記錄GC前后card的信息。所以綜合這兩種情況暇屋,CMS Collector在remark階段會(huì)從Mod Union Table和Card Table出發(fā)似袁;
- 因?yàn)樵诨厥针A段還有可能分配對(duì)象,所以垃圾回收不能等內(nèi)存滿了才開(kāi)始咐刨,必須要提前開(kāi)始昙衅。但是CMS Collector并不提供在回收階段一定含有足夠的空閑給應(yīng)用分配對(duì)象。這就會(huì)造成一個(gè)問(wèn)題定鸟,就是在垃圾回收階段而涉,空閑空間不足了。這個(gè)時(shí)候联予,應(yīng)用會(huì)被停下來(lái)啼县,也就是進(jìn)入到stop-the-world狀態(tài),直到回收完成沸久;
- 該回收的垃圾沒(méi)有被回收季眷。這也被稱為floating garbage。這主要是出現(xiàn)在卷胯,原本一個(gè)對(duì)象被標(biāo)記為存活子刮,但是在concurrent階段,應(yīng)用修改了指向該對(duì)象的引用诵竭,使得它稱為了垃圾话告。但是CMS Collector無(wú)法將其檢測(cè)出來(lái)兼搏。因此它能夠躲過(guò)這一輪的垃圾回收,直到下一次的回收周期沙郭;
CMS Collector不是compacting的佛呻,這意味著垃圾回收之后得到的空閑空間并不是連續(xù)的。這會(huì)帶來(lái)一些問(wèn)題:
- 分配方式改變:前面提到的最多的分配方式就是bump-the-pointer病线。但是該分配方式只使用于連續(xù)的空閑空間吓著。CMS采用了新的分配方式:空閑鏈表分配方式,該概念和操作系統(tǒng)中內(nèi)存管理中的空閑鏈表是一樣的送挑;
- 空閑空間不連續(xù)會(huì)導(dǎo)致空間有效利用率下降绑莺。比如說(shuō)空閑空間總和是足以容納分配對(duì)象的,但是因?yàn)椴荒苋菁{惕耕,所以反而會(huì)觸發(fā)GC纺裁,或者會(huì)觸發(fā)擴(kuò)容。所以對(duì)于CMS Collector來(lái)說(shuō)司澎,它會(huì)要求更大的堆欺缘;
- CMS Collector會(huì)合并相鄰的空閑空間:這是一個(gè)優(yōu)化,但是因?yàn)楹喜⑦@個(gè)空閑空間需要操作空閑鏈表挤安,而分配對(duì)象又需要操作空閑鏈表谚殊,所以在concurrent sweep會(huì)出現(xiàn)空閑鏈表的爭(zhēng)用。CMS Collector使用了Mutual exclusion locks來(lái)保證JVM分配優(yōu)先蛤铜;
CMS Collector是允許整理空閑空間的嫩絮,用戶可以通過(guò)命令行選項(xiàng)UseCMSCompactAtFullCollection來(lái)指定。
CMS Collector還有一種模式围肥,增量模式(Incremental Mode)剿干。在該模式下,CMS Collector會(huì)使用少量的線程來(lái)并發(fā)標(biāo)記或者并發(fā)清掃虐先,整個(gè)過(guò)程會(huì)持續(xù)多個(gè)minor collection周期怨愤。該模式的好處是,可以降低pause time蛹批,并且減少對(duì)應(yīng)用的影響撰洗。所付出的代價(jià),就是需要更加大的堆腐芍。該模式一般適用于CPU數(shù)量較少的情況差导。
使用場(chǎng)景
適用于要求pause time盡可能短,并且擁有多個(gè)CPU的應(yīng)用猪勇。CMS Collector的別名是Latency Collector设褐。
影響因素和選項(xiàng)
在Serial Collector中談及的因素對(duì)CMS Collector都會(huì)有影響,此外還受到:
- 線程數(shù)量
- CPU數(shù)量
- CMSInitiatingOccupancyFraction:該選項(xiàng)設(shè)置了當(dāng)被分配內(nèi)存占據(jù)了多大比例的時(shí)候,就會(huì)觸發(fā)major collection助析。若是設(shè)置的太小犀被,那么會(huì)導(dǎo)致更加頻繁的GC;但是設(shè)置得太大外冀,就更有可能出現(xiàn)回收過(guò)程中空閑空間不足的現(xiàn)象寡键,而這會(huì)導(dǎo)致應(yīng)用被停下來(lái),直到GC完成雪隧;
- 是否使用增量模式
G1 Collector
原理
G1 Collector(Garbage-First Collector)可以被看做是CMS Collector的升級(jí)加強(qiáng)版西轩。G1 Collector的算法流程和CMS類似,所不同的有:
- G1 Collector采用的是標(biāo)記-整理算法脑沿。這意味著每次算法結(jié)束得到的都是連續(xù)空間藕畔;
- G1 Collector雖然還采用分代的方式,但是它的內(nèi)存模型有了巨大的變化庄拇。它的內(nèi)存基本結(jié)構(gòu)被分成了一個(gè)個(gè)Region注服。G1 Collector維護(hù)了一個(gè)Region的列表,每次判斷哪個(gè)Region的回收價(jià)值最大丛忆,便回收該Region祠汇。也就是說(shuō),G1 Collector回收并不是回收整個(gè)區(qū)域熄诡,而是分區(qū)域收集的;
其具體流程是:
- initial marking phase:標(biāo)記根诗力,該階段是stop-the-world式的凰浮;
- root region scanning phase:該階段標(biāo)記Survivor Region中的指向老年代的引用,及其引用對(duì)象苇本;
- Concurrent marking phase:
- Remark phase:
- Cleanup phase:
所以CMS Collector因?yàn)椴l(fā)引發(fā)的問(wèn)題G1也同樣存在袜茧。但是G1 Collector能夠避開(kāi)各種因?yàn)榭臻e空間不連續(xù)所導(dǎo)致的問(wèn)題。
G1 Collector實(shí)現(xiàn)的算法是比較復(fù)雜的瓣窄,詳細(xì)內(nèi)容可以閱讀Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide和《垃圾回收算法手冊(cè)-自動(dòng)內(nèi)存管理的藝術(shù)》10.3.1笛厦,11.8.6等
G1 Collector有一個(gè)很重要的特性,就是“軟實(shí)時(shí)”俺夕。G1 Collector可以讓使用者指定在一個(gè)長(zhǎng)度為M毫秒的時(shí)間段內(nèi)裳凸,消耗在垃圾收集上的時(shí)間不得超過(guò)N毫秒。這已經(jīng)是期望達(dá)到一種類似于實(shí)時(shí)垃圾回收的效果了劝贸。
所謂的實(shí)時(shí)垃圾回收姨谷,是指必須能夠精確地控制由垃圾回收所導(dǎo)致的賦值器的中斷。
結(jié)語(yǔ)
強(qiáng)烈推薦閱讀Oracle上關(guān)于HotSpot的相關(guān)文檔映九,以及《垃圾回收算法手冊(cè)-自動(dòng)內(nèi)存管理的藝術(shù)》梦湘。