Hotspot垃圾回收
Hotspot VM 使用分代回收算法(Generational Collector)
??
GC堆的分代:
(1)Young Generation(年青代):大多數(shù)對象
?????? Eden區(qū)+Survivor_1區(qū)+Survivor_2區(qū)
(2)Old Generation(年長代):年青代的幸存對象(若干次回收后剩下的對象)+大對象
(3)Permanent Generation(持久代):元信息對象(ClassObject,Method...)
??
快速分配(Fast Allocation)????
bump-the-pointer:尾部指針(指向之前已分配對象的尾部)
???? 分配時(shí)比較(空間尾部和尾部指針的差值)剩余空間大小
???
線程局部分配緩沖(Thread-Local Allocation Buffers):
???? 每個(gè)線程有一個(gè)獨(dú)立的分配緩沖(Eden區(qū)的一部分),再使用bump-the-pointer完成分配
??
??
1.串行回收(serial collector)
使用串行收集器時(shí),對年幼代和年長代的收集都采用串行、STW方式進(jìn)行。也就是說收集任務(wù)同時(shí)只使用一個(gè)CPU,而且在收集任務(wù)執(zhí)行期間,應(yīng)用程序的執(zhí)行被中止。
(1)年青代
????首先把 eden中的活動(dòng)對象復(fù)制到空閑的 survivor space中,即圖中標(biāo)為 To的區(qū)域,其中的一些大對象會(huì)被直接復(fù)制到老年代中。另一個(gè) survivor space(標(biāo)為 From)中仍然年輕的對象同樣會(huì)被復(fù)制到 To 中,而其中的一些年老的對象會(huì)被復(fù)制到老年代中。注意,在向 To中復(fù)制對象的時(shí)候,如果 eden或 From中還有一些對象沒有被復(fù)制的時(shí)候 To已經(jīng)被填滿了,那么 eden和 From中的這些對象不管它經(jīng)歷過幾次新生代垃圾收集都會(huì)被直接復(fù)制到老年代中。eden和From 中剩余的對象就成為了垃圾,它們所占用的內(nèi)存將會(huì)被釋放。
??? 經(jīng)過一次新生代垃圾收集后,eden和 From都是空閑的,只有 To中含有活動(dòng)的對象,此時(shí),將交換兩個(gè) survivor space,即 From 被標(biāo)識(shí)為 To,To則為 From
(2)年長代
在串行垃圾收集器中,老年代和永生代使用標(biāo)記清掃壓縮(mark-sweep-compact)算法。在標(biāo)記階段,垃圾收集器標(biāo)識(shí)出哪些對象是活動(dòng)對象。清掃階段則標(biāo)識(shí)出垃圾對象。最后垃圾收集器執(zhí)行平移壓縮,把活動(dòng)對象向老年代或永生代的一端平移,這樣老年代或永生代的另一端就是一塊連續(xù)的空閑內(nèi)存。
??
2.并行回收(parallel collector)
目前,許多Java應(yīng)用的運(yùn)行平臺(tái)大都包含很多物理內(nèi)存和多個(gè)CPU。并行收集器,也被稱作吞吐量收集器,被開發(fā)出來的主要目的就是為了充分利用CPU資源,而不是只讓一個(gè)CPU去做垃圾收集而其他CPU卻被閑置。
(1)年青代
和串行收集器相比,并行收集器采用了大致相同的年幼代收集算法,只是執(zhí)行的是其并行版本而已。對年幼代的收集雖然仍基于拷貝技術(shù)、采用STW方式進(jìn)行,但收集工作是并行展開的,使用了多個(gè)CPU,這就降低了垃圾收集開銷,從而提高了應(yīng)用程序的吞吐量。下圖展示了并行收集器和串行收集器在執(zhí)行年幼代收集時(shí)到底有何不同:
(2)年長代
和串行收集器一樣,并行收集器對年長代的收集同樣基于標(biāo)記-清理-壓縮算法,同樣采用串行、STW方式進(jìn)行。
??
3.并行緊縮回收(parallel compacting collector)
????? (1)年青代
????? 同并行回收的年青代
????? (2)年長代
????? 平行壓縮垃圾收集器在老年代和永生代中并行地執(zhí)行 stop-the-world式的垃圾收集,并進(jìn)行平移壓縮。這種垃圾收集器的垃圾收集過程分為三個(gè)階段。在標(biāo)記階段,垃圾收集器首先會(huì)把每個(gè)代邏輯上劃分為固定大小的區(qū)域 ( region ),這樣應(yīng)用程序中的強(qiáng)可達(dá)對象就分布于不同的垃圾收集線程中,垃圾收集線程可以并行地標(biāo)記活動(dòng)對象。一旦一個(gè)對象被標(biāo)記為活動(dòng)的,垃圾收集器會(huì)更新這個(gè)對象所在區(qū)域的數(shù)據(jù)結(jié)構(gòu),例如對象的大小及位置。
??????匯總階段的的操作是針對區(qū)域( region )的,而不是對象。由于之前進(jìn)行的垃圾收集操作,每個(gè)代的左端活動(dòng)對象的密度較高。從這種活動(dòng)對象密度較高的區(qū)域中壓縮回收空間所需的開銷很大,但釋放回收的內(nèi)存空間卻很有限,這樣對這些區(qū)域進(jìn)行壓縮垃圾收集是很不劃算的。因此在匯總階段垃圾收集器從代的左端開始檢查每個(gè)區(qū)域的活動(dòng)對象的密度,直到發(fā)現(xiàn)這樣一個(gè)區(qū)域,壓縮回收這個(gè)區(qū)域以及這個(gè)區(qū)域右邊的區(qū)域中的空間開銷很小。當(dāng)找到這樣一個(gè)區(qū)域后,這個(gè)區(qū)域左邊的區(qū)域會(huì)稱為 dense prefix,而且不會(huì)有對象復(fù)制到這些區(qū)域中。對這個(gè)區(qū)域右邊的區(qū)域進(jìn)行壓縮,并回收垃圾對象所占用的內(nèi)存空間。匯總階段會(huì)計(jì)算并保存壓縮后的區(qū)域中對象的新地址。注意,當(dāng)前匯總階段的實(shí)現(xiàn)是串行的,當(dāng)然匯總也可以實(shí)現(xiàn)為并行的,但相對于性能匯總階段的并行不及標(biāo)記壓縮階段來得重要。
??????在壓縮階段,垃圾收集線程使用匯總階段生成的數(shù)據(jù)標(biāo)識(shí)出可以進(jìn)行壓縮填充的區(qū)域,這些線程并行地進(jìn)行數(shù)據(jù)復(fù)制,從一個(gè)區(qū)域復(fù)制到另一個(gè)區(qū)域。這樣就使堆的一端有大量的活動(dòng)對象,而另一端則是一塊大的空閑內(nèi)存區(qū)域。?
?? ??
4.并發(fā)標(biāo)記清理回收(Concurrent Mark-Sweep Collector)CMS
????? (1)年青代
????? 同并行回收的年青代
????? (2)年長代
??????老年代垃圾收集的大部分操作是與應(yīng)用程序一起并發(fā)地執(zhí)行的。
? ?? ?CMS垃圾收集器進(jìn)行老年代垃圾收集時(shí)首先會(huì)進(jìn)行初始標(biāo)記,在初始標(biāo)記階段 cms垃圾收集器會(huì)標(biāo)識(shí)應(yīng)用中的強(qiáng)可達(dá)對象,這個(gè)階段會(huì)暫停應(yīng)用的執(zhí)行。接著 cms垃圾收集器進(jìn)行并發(fā)標(biāo)記,并發(fā)標(biāo)記從初始標(biāo)記階段標(biāo)識(shí)的活動(dòng)對象開始,遞歸地遍歷并標(biāo)識(shí)這些對象中引用的強(qiáng)可達(dá)對象。由于并發(fā)標(biāo)記時(shí)應(yīng)用程序也在執(zhí)行,運(yùn)行中的應(yīng)用程序可能會(huì)更新對象的引用,所以并發(fā)標(biāo)記不能標(biāo)識(shí)出內(nèi)存中的全部活動(dòng)對象。為了解決這個(gè)問題應(yīng)用程序會(huì)被再次暫停,cms垃圾收集器再次訪問在并發(fā)標(biāo)記過程中引用被修改過的對象,這稱為重新標(biāo)記。為了提高重新標(biāo)記的效率,多個(gè)線程會(huì)并行地執(zhí)行重新標(biāo)記。??
??????重新標(biāo)記執(zhí)行完后,堆中所有的活動(dòng)對象都被標(biāo)識(shí)出來了,接下來的并發(fā)清掃階段垃圾收集器會(huì)回收堆中的垃圾對象
????? 像重新標(biāo)記這樣的任務(wù)會(huì)增加垃圾收集的工作量,這也增加了垃圾收集的開銷,所以降低暫停時(shí)間是通過增加垃圾收集的開銷來獲得的。
????? cms 垃圾收集器是唯一一個(gè)不進(jìn)行緊縮的垃圾收集器,當(dāng) cms釋放了垃圾對象占用的內(nèi)存后,它不會(huì)把活動(dòng)對象移動(dòng)到老年代的一端。
這樣做減少了 cms垃圾收集器進(jìn)行垃圾收集花費(fèi)的時(shí)間,但由于空閑的內(nèi)存不是連續(xù)的,無法使用一個(gè)簡單的指針來標(biāo)識(shí)出下一個(gè)可以用來分配的空閑內(nèi)存。cms垃圾收集器使用了一個(gè)空閑內(nèi)存鏈表把空閑的內(nèi)存鏈接在一起,每次分配內(nèi)存的時(shí)候,垃圾收集器會(huì)在這個(gè)鏈表中查找出第一個(gè)能滿足分配需要的空閑內(nèi)存,所以從老年代中分配對象是十分昂貴的。這同樣會(huì)影響到新生代垃圾收集,因?yàn)槔夏甏械拇蟛糠謱ο笫窃谛律占臅r(shí)候從新生代晉升為老年代的。
?
?? ?? cms垃圾收集器的另一個(gè)缺點(diǎn)是它需要的堆空間比其他的垃圾收集器要大。由于在標(biāo)記階段應(yīng)用程序的執(zhí)行并沒有被暫停,應(yīng)用程序執(zhí)行過程中會(huì)繼續(xù)申請內(nèi)存,這樣在垃圾收集的過程中老年代處于增長狀態(tài)。另外,雖然垃圾收集器保證能夠識(shí)別所有的活動(dòng)對象,但有一些對象在標(biāo)記過程中成為了垃圾,而這些對象在此次垃圾收集過程中并不會(huì)被回收,要等到下次垃圾收集才會(huì)被回收。這樣的對象被稱為?floating garbage?。
?
? ?? ?由于不執(zhí)行壓縮所以 cms 垃圾收集器很容易引起內(nèi)存碎片。為了解決這個(gè)問題,cms會(huì)追蹤常用的對象大小,預(yù)估內(nèi)存需求,有時(shí)會(huì)分隔或合并空閑內(nèi)存塊。
?
??????與其它垃圾收集器不同,cms垃圾收集器不會(huì)在老年代被填滿的時(shí)候執(zhí)行垃圾收集。相反,它會(huì)在老年代被填滿之前盡早地執(zhí)行垃圾收集,否則這就與串行或并行垃圾收集器一樣會(huì)造成應(yīng)用長時(shí)間地暫停。為了避免這種情況,cms會(huì)根據(jù)一些統(tǒng)計(jì)信息來決定執(zhí)行垃圾收集的時(shí)機(jī)或者當(dāng)老年代的使用達(dá)到某個(gè)伐值的時(shí)候也會(huì)啟動(dòng) cms垃圾收集,通過命令行參數(shù) -XX:CMSInitiatingOccupancyFraction = n設(shè)置初始伐值,其中 n是老年代大小的百分比,當(dāng)來年代的使用達(dá)到這個(gè)伐值時(shí) cms會(huì)啟動(dòng),n默認(rèn)為 68。
?
??? ??總體來看,與平行垃圾收集器相比,cms減少了執(zhí)行老年代垃圾收集時(shí)應(yīng)用暫停的時(shí)間,但卻增加了新生代垃圾收集時(shí)應(yīng)用暫停的時(shí)間、降低了吞吐量而且需要占用更大的堆空間
??
??
??
Hotspot JVM中的垃圾回收?
????
???
總結(jié)
以上是生活随笔為你收集整理的Hotspot垃圾回收的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Makefile分析
- 下一篇: VirtualAlloc和Virtual