JVM(HotSpot) 垃圾收集器
需要提前了解的知識(shí):
1. JVM內(nèi)存模型
2. JVM垃圾回收算法
HotSpot虛擬機(jī)所有的垃圾收集器如下圖:
上面有7種收集器,分為部分,上面為新生代收集器,下面是老年代收集器。如果兩個(gè)收集器之間存在連線,就說明它們可以搭配使用。
新生代的收集器使用復(fù)制算法,
老年代使用并發(fā)標(biāo)記清除(CMS)或標(biāo)記-整理算法。
Stop The World
Java中Stop-The-World機(jī)制簡稱STW,是在執(zhí)行垃圾收集算法時(shí),Java應(yīng)用程序的其他所有線程都被掛起(除了垃圾收集幫助器之外)。Java中一種全局暫停現(xiàn)象,全局停頓,所有Java代碼停止,native代碼可以執(zhí)行,但不能與JVM交互。
垃圾收集器
| 1 | Serial | 新生代 | 復(fù)制 | 單線程 |
| 2 | ParNew | 新生代 | 復(fù)制 | 多線程并行 |
| 3 | Parallel | 新生代 | 復(fù)制 | 多線程并行 |
| 4 | Serial Old | 老年代 | 標(biāo)記整理 | 單線程 |
| 5 | CMS | 老年代 | 標(biāo)記清除 | 多線程并發(fā) |
| 6 | Parallel Old | 老年代 | 標(biāo)記整理 | 多線程 |
| 7 | G1 | 全部 | 復(fù)制算法,標(biāo)記-整理 | 多線程 |
解釋:
并行(Parallel):多條垃圾收集線程并行工作,而用戶線程仍處于等待狀態(tài)
并發(fā)(Concurrent):垃圾收集線程與用戶線程一段時(shí)間內(nèi)同時(shí)工作(交替執(zhí)行)
1、Serial(串行GC)收集器
Serial收集器是一個(gè)新生代收集器,單線程執(zhí)行,使用復(fù)制算法。它在進(jìn)行垃圾收集時(shí),必 須暫停其他所有的工作線程(用戶線程)。是Jvm client模式下默認(rèn)的新生代收集器。對(duì)于限定單個(gè)CPU的環(huán)境來說,Serial收集器由于沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單 線程收集效率。
2、ParNew(并行GC)收集器
ParNew收集器其實(shí)就是serial收集器的多線程版本,除了使用多條線程進(jìn)行垃圾收集之外,其余行為與Serial收集器一樣。
3、Parallel Scavenge(并行回收GC)收集器
Parallel Scavenge收集器也是一個(gè)新生代收集器,它也是使用復(fù)制算法的收集器,又是并行多線程收集器。parallel Scavenge收集器的目標(biāo)則是達(dá)到一個(gè)可控制的吞吐量。吞吐量= 程序運(yùn)行時(shí)間/(程序運(yùn)行時(shí)間 + 垃圾收集時(shí)間),虛擬機(jī)總共運(yùn)行了100分鐘。其中垃圾收集花掉1分鐘,那吞吐量就是99%。
使用如下2個(gè)參數(shù)進(jìn)行控制吞吐量
1. -XX:MaxGCPauseMillis
MaxGCPauseMillis參數(shù)允許的值是一個(gè)大于0的毫秒數(shù),收集器將盡力保證內(nèi)存回收花費(fèi)的時(shí)間不超過設(shè)定值。
2. -XX:GCTimeRatio
GCTimeRatio參數(shù)的值應(yīng)當(dāng)是一個(gè)大于0小于100的整數(shù),也就是垃圾收集時(shí)間占總時(shí)間的比率,相當(dāng)于是吞吐量的倒數(shù)。如果把此參數(shù)設(shè)置為19,那允許的最大GC時(shí)間就占總時(shí)間的5%(即1 /(1+19)),默認(rèn)值為99,就是允許最大1%(即1 /(1+99))的垃圾收集時(shí)間。
示意圖和ParNew類似(參見圖2)。
4、Serial Old(串行GC)收集器
Serial Old是Serial收集器的老年代版本,它同樣使用一個(gè)單線程執(zhí)行收集,使用“標(biāo)記-整理”算法。主要使用在Client模式下的虛擬機(jī)。如果在Server模式下,它主要還有兩大用途:一個(gè)是在JDK 1.5及之前的版本中與Parallel Scavenge收集器搭配使用,另外一個(gè)就是作為CMS收集器的后備預(yù)案。如果CMS收集器出現(xiàn)Concurrent Mode Failure,則Serial Old收集器將作為后備收集器。
詳見圖1 老年的收集
5、Parallel Old(并行GC)收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法。
6、CMS(并發(fā)GC)收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,整個(gè)收集過程大致分為4個(gè)步驟:
1. 初始標(biāo)記(CMS initial mark)
2. 并發(fā)標(biāo)記(CMS concurrenr mark)
3. 重新標(biāo)記(CMS remark)
4. 并發(fā)清除(CMS concurrent sweep)
其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟任然需要停頓其他用戶線程。初始標(biāo)記僅僅只是標(biāo)記出GC ROOTS能直接關(guān)聯(lián)到的對(duì)象,速度很快,并發(fā)標(biāo)記階段是進(jìn)行GC ROOTS 根搜索算法階段,會(huì)判定對(duì)象是否存活。而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間會(huì)被初始標(biāo)記階段稍長,但比并發(fā)標(biāo)記階段要短。
由于整個(gè)過程中耗時(shí)最長的并發(fā)標(biāo)記和并發(fā)清除過程中,收集器線程都可以與用戶線程一起工作,所以整體來說,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。
CMS收集器的優(yōu)點(diǎn):并發(fā)收集、低停頓,但是CMS還遠(yuǎn)遠(yuǎn)達(dá)不到完美,器主要有三個(gè)顯著缺點(diǎn):
CMS收集器對(duì)CPU資源非常敏感。
在并發(fā)階段,雖然不會(huì)導(dǎo)致用戶線程停頓,但是會(huì)占用CPU資源而導(dǎo)致引用程序變慢,總吞吐量下降。CMS默認(rèn)啟動(dòng)的回收線程數(shù)是:(CPU數(shù)量+3)/4。(建議CPU個(gè)數(shù)最少4個(gè))。
無法處理浮動(dòng)垃圾
在做垃圾回收的過程中會(huì)產(chǎn)生新的垃圾(并行執(zhí)行),所以需要預(yù)留一部分空間給用戶線程使用。
可以使用-XX:CMSInitiatingOccupancyFraction(jdk1.6 默認(rèn)為92%)參數(shù)來設(shè)置,預(yù)留多少空間開始做GC。如果在垃圾回收的過程中,剩余空間不足仍然滿足不了用戶線程生成對(duì)象所需要的空間,就會(huì)出現(xiàn)“Concurrent Mode Failure”失敗,這時(shí)候虛擬機(jī)將啟動(dòng)后備預(yù)案:臨時(shí)啟用Serial Old收集器來重新進(jìn)行老年代的垃圾收集,這樣停頓時(shí)間就很長了。
CMS是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的收集器,使用“標(biāo)記-清除”算法收集后,會(huì)產(chǎn)生大量碎片。
空間碎片太多時(shí),將會(huì)給對(duì)象分配帶來很多麻煩,比如說大對(duì)象,內(nèi)存空間找不到連續(xù)的空間來分配不得不提前觸發(fā)一次Full GC。為了解決這個(gè)問題,CMS收集器提供了一個(gè)-XX:UseCMSCompactAtFullCollection開關(guān)參數(shù),用于在Full GC之后增加一個(gè)碎片整理過程,還可通過-XX:CMSFullGCBeforeCompaction參數(shù)設(shè)置執(zhí)行多少次不壓縮的Full GC之后,跟著來一次碎片整理過程。
7、G1收集器
G1(Garbage First)垃圾回收器是用在heap memory很大的情況下,把heap劃分為很多很多的region塊,然后并行的對(duì)其進(jìn)行垃圾回收。
G1垃圾回收器在清除實(shí)例所占用的內(nèi)存空間后,還會(huì)做內(nèi)存壓縮。
G1垃圾回收器回收region的時(shí)候基本不會(huì)STW,而是基于 most garbage優(yōu)先回收 的策略來對(duì)region進(jìn)行垃圾回收的。
結(jié)果如下圖:
一個(gè)region有可能屬于Eden,Survivor或者Tenured內(nèi)存區(qū)域。圖中的E表示該region屬于Eden內(nèi)存區(qū)域,S表示屬于Survivor內(nèi)存區(qū)域,T表示屬于Tenured內(nèi)存區(qū)域。圖中空白的表示未使用的內(nèi)存空間。G1垃圾收集器還增加了一種新的內(nèi)存區(qū)域,叫做Humongous內(nèi)存區(qū)域,如圖中的H塊。這種內(nèi)存區(qū)域主要用于存儲(chǔ)大對(duì)象-即大小超過一個(gè)region大小的50%的對(duì)象。
年輕代垃圾收集
在G1垃圾收集器中,年輕代的垃圾回收過程使用復(fù)制算法。把Eden區(qū)和Survivor區(qū)的對(duì)象復(fù)制到新的Survivor區(qū)域。
如下圖:
老年代垃收集
對(duì)于年老代上的垃圾收集,G1垃圾收集器也分為4個(gè)階段,基本跟CMS垃圾收集器一樣,但略有不同:
Initial Mark階段 - 同CMS垃圾收集器的Initial Mark階段一樣,G1也需要暫停應(yīng)用程序的執(zhí)行,它會(huì)標(biāo)記從根對(duì)象出發(fā),在根對(duì)象的第一層孩子節(jié)點(diǎn)中標(biāo)記所有可達(dá)的對(duì)象。但是G1的垃圾收集器的Initial Mark階段是跟minor gc一同發(fā)生的。也就是說,在G1中,你不用像在CMS那樣,單獨(dú)暫停應(yīng)用程序的執(zhí)行來運(yùn)行Initial Mark階段,而是在G1觸發(fā)minor gc的時(shí)候一并將年老代上的Initial Mark給做了。
Concurrent Mark階段 - 在這個(gè)階段G1做的事情跟CMS一樣。但G1同時(shí)還多做了一件事情,就是如果在Concurrent Mark階段中,發(fā)現(xiàn)哪些Tenured region中對(duì)象的存活率很小或者基本沒有對(duì)象存活,那么G1就會(huì)在這個(gè)階段將其回收掉,而不用等到后面的clean up階段。這也是Garbage First名字的由來。同時(shí),在該階段,G1會(huì)計(jì)算每個(gè) region的對(duì)象存活率,方便后面的clean up階段使用 。
Remark階段 - 在這個(gè)階段G1做的事情跟CMS一樣, 但是采用的算法不同,G1采用一種叫做SATB(snapshot-at-the-begining)的算法能夠在Remark階段更快的標(biāo)記可達(dá)對(duì)象。
Clean up/Copy階段 - 在G1中,沒有CMS中對(duì)應(yīng)的Sweep階段。相反 它有一個(gè)Clean up/Copy階段,在這個(gè)階段中,G1會(huì)挑選出那些對(duì)象存活率低的region進(jìn)行回收,這個(gè)階段也是和minor gc一同發(fā)生的,如下圖所示:
本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
點(diǎn)擊這里快速進(jìn)入簡書
GIT地址:http://git.oschina.net/brucekankan/
點(diǎn)擊這里快速進(jìn)入GIT
總結(jié)
以上是生活随笔為你收集整理的JVM(HotSpot) 垃圾收集器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM 内存区域大小参数设置
- 下一篇: HashMap实现中文分词器