小胖在寫完這篇文章后,問自己的幾個問題稻扬。我想卦方,看完這篇文章,你可能就會有自己的答案了泰佳。
- 1次10msGC和10次1ms的GC你會選擇哪種盼砍?
- CMS收集器缺陷是什么?
- 提高CMS的啟動閾值一定能提高性能嗎?為什么逝她?
- G1收集器為什么可以設(shè)置停頓時間浇坐?
- GC如何確定對象是否可被回收?
- 針對你說的“可達性分析法”黔宛,Minor GC時會掃描整個堆嗎近刘?
- JDK8默認的垃圾收集器是什么?
JVM那點事-垃圾收集算法講了GC垃圾回收算法臀晃,即(
分代收集算法[復(fù)制算法——標記清除/標記整理]
)觉渴。我們接下來講一下垃圾回收器。下圖展示了不同分代的收集器徽惋,如果兩個收集器之間存在連線案淋,說明他們可以搭配使用。虛擬機所在的區(qū)域寂曹,則表示它是屬于新生代收集器還是老年代收集器哎迄。
1. Serial收集器(單線程收集器)
標簽:新生代收集器
復(fù)制算法
單線程收集器
Serial收集器[?s?ri?l]
是單線程新生代收集器
回右,進行垃圾回收的時候隆圆,Stop the World
暫停其他所有的工作線程。對于單個CPU
的環(huán)境來說翔烁,簡單而高效渺氧。(虛擬機運行在Client模式下的默認新生代收集器。)
2. ParNew收集器(并行收集器)
標簽:新生代收集器
復(fù)制算法
多線程收集器
CMS默認收集器
ParNew([?p?r]
)收集器其實就是Serial
收集器的多線程版本蹬屹。ParNew
是許多運行在Server模式下虛擬機
中首選的新生代收集器
侣背。一個重要原因是:目前除了Serial
收集器外,只有它與CMS收集器配合工作慨默。ParNew收集器也是使用-XX:UseConcMarkSweepGC
選項的默認收集器贩耐,也可以使用-XX:+UseParNewGC
選項強制指定。
ParNew
收集器在單個CPU環(huán)境中絕對不會有比Serial
收集器更好的效果厦取。默認開啟的收集器線程數(shù)與CPU的數(shù)量相同潮太。可以使用-XX:ParallelGCThreads
參數(shù)限制垃圾收集器的線程數(shù)[?p?r?lel]
。
并行(Parallel):指多條垃圾收集器線程并行工作铡买,但此時用戶線程仍處于等待狀態(tài)更鲁。
并發(fā)(Concurrent):指用戶線程與垃圾回收器同時執(zhí)行(但不一定是并行的,可能會交替運行)奇钞,用戶程序在繼續(xù)運行澡为,而垃圾手機程序運行在另一個CPU上。
3. Parallel Scavenge收集器(并行收集器)
標簽:新生代收集器
復(fù)制算法
多線程收集器
吞吐量優(yōu)先
[?p?r?lel][?sk?v?nd?] 并發(fā)清掃
景埃;Parallel Scavenge
目標是達到一個可控制的吞吐量媒至。(吞吐量=用戶代碼運行時間/虛擬機運行時間,例如程序運行了100分鐘谷徙,GC占用1分鐘塘慕,那么吞吐量就是99%)
(敲黑板,畫重點:面試官問道:“小胖同學(xué)蒂胞,1次10ms的GC和10次1ms的GC图呢,你會選哪種?”)
- 停頓時間短:適合與 用戶交互的程序骗随,良好的響應(yīng)速度提升用戶體驗阱持。
- 高吞吐量:適合后臺運算而不需要太多交互的任務(wù),可以高效率利用CPU時間齿诞,盡快完成程序的運行任務(wù).
(小胖內(nèi)心OS:停頓時間1ms麻裳,這么快,一定是垃圾少涨椒,那就是犧牲了新生代空間
摊鸡,頻繁GC會導(dǎo)致吞吐量的下降
,但是適合用戶交互程序蚕冬,反之免猾,吞吐量大的話,可以高效利用CPU
囤热,適合后臺運算的程序猎提。)
Parallel Scavenge
收集器提供了兩個參數(shù)用于精確控制吞吐量∨园控制垃圾回收器停頓時間的-XX:MaxGCPauseMills
[p?:z]['m?lz]
锨苏。以及設(shè)置吞吐量大小的-XX:GCTimeRatio
不要以為把MaxGCPauseMills
參數(shù)設(shè)置小就能使系統(tǒng)垃圾收集速度變快。GC
停頓時間縮短是犧牲吞吐量和新生代空間來換取的棺聊。(比如將新生代調(diào)小伞租,原來10s收集一次,每次100ms限佩,葵诈;現(xiàn)在5s收集一次,每次70ms;停頓時間的確下降驯击,但吞吐量也下降了)
GCTimeRatio
參數(shù)的值應(yīng)當是[0,100]的整數(shù)烁兰。如果把參數(shù)設(shè)置為19,允許最大的GC時間(1/(1+19))
占用總時間的5%
4. Parallel Old收集器(并行收集器)
標簽:老生代收集器
標記-整理算法
多線程收集器
吞吐量優(yōu)先
注重吞吐量以及CPU資源敏感的場合徊都,都可以優(yōu)先考慮Parallel Scavenge
加Parallel Old
收集器沪斟。
5. CMS收集器(并發(fā)收集器)
標簽: 老年代收集器
標記-清除算法
低停頓時間
并發(fā)收集器
CPU資源敏感
浮動垃圾
內(nèi)存碎片
CMS(Concurrent Mark Sweep)
收集器是一種獲取最短回收停頓時間為目標的收集器。目前很大一部分的java應(yīng)用集中在互聯(lián)網(wǎng)或者B/S系統(tǒng)的服務(wù)端上暇矫。重視響應(yīng)速度主之,希望系統(tǒng)停頓時間最短。
- 初始標記:標記GC Roots 直接關(guān)聯(lián)的對象(Stop the World)李根。
-
并發(fā)標記:獲取
初始標記
的節(jié)點做為根節(jié)點槽奕,并發(fā)標記對象。 -
重新標記:修正
并發(fā)標記
過程中變動的對象房轿。如何修改粤攒?就是將并發(fā)標記
階段變化的對象記錄在線程Remembered Set Logs
線程,在重新標記
階段囱持,將數(shù)據(jù)合并到Remember Set
中夯接。(PS:詳見G1收集器描述)(Stop the World) -
并發(fā)清除:
并發(fā)清除
對象
(原理簡單明了)
其中初始標記
、重新標記
兩個步驟仍然需要Stop The World
纷妆。
缺點是:
對CPU資源敏感:在并發(fā)階段,雖然不會導(dǎo)致用戶線程停頓掩幢,但是占用線程(CPU資源)導(dǎo)致應(yīng)用程序變慢逊拍。CMS默認啟動的線程數(shù)
(CPU+3)/4
,也就是當CPU在4個以上時际邻,并發(fā)回收垃圾線程不少于25%的CPU資源芯丧。浮動垃圾:
并發(fā)清理
階段用戶線程還在運行,只能下次GC回收枯怖,這些就稱為“浮動垃圾”
注整。垃圾收集階段
用戶線程還在運行能曾,所以不能等到老年代幾乎填滿在進行收集度硝。要設(shè)置老年代
預(yù)留空間,可以設(shè)置-XX:CMSInitiatingOccupancyFranction
音標:[?'n???e?t??]初始 [??kj?p?nsi] 居住 [?fr?k?n] 一小部分
寿冕,提高觸發(fā)百分比蕊程。在JDK1.6,CMS收集器的啟動闕值92%
驼唱。要是CMS運行期間預(yù)留的內(nèi)存無法滿足程序需要藻茂,就會觸發(fā)Concurrent Mode Failure
失敗,虛擬機啟動后備方案,臨時啟動Serial Old
收集器來重新進行老年代的垃圾回收辨赐。所以說优俘,參數(shù)-XX:CMSInitiatingOccupancyFranction
設(shè)置的太高,容易觸發(fā)Concurrent Mode Failure
失敗掀序,性能反而降低帆焕。內(nèi)存碎片:“標記-清除”算法會產(chǎn)生大量空間碎片!空間碎片過多時不恭,大對象分配帶來麻煩叶雹,導(dǎo)致對象有很大剩余,但是無法找到連續(xù)空間分配對象换吧,提前觸發(fā)full GC折晦。
可以設(shè)置-XX:UseCMSCompactAtFullCollection
CMS默認開啟。在Full GC
的時候采用合并整理
過程沾瓦,但是內(nèi)存整理無法并發(fā)會導(dǎo)致停頓時間變長满着。還有一個參數(shù)-XX:CMSFullGCsBeforeCompaction
,這個參數(shù)是用于執(zhí)行多少次不壓縮的Full GC
跟著來一次壓縮的贯莺。
總結(jié)來說:CMS并發(fā)收集漓滔,低停頓。
- 搶占CPU資源乖篷;
- 浮動垃圾响驴,并且要為
應(yīng)用程序預(yù)留空間
,我們可以設(shè)置預(yù)留比例撕蔼,但是預(yù)留比例無法滿足應(yīng)用程序需求時豁鲤,會啟動Serial Old
收集器。性能可能下降;- 內(nèi)存碎片鲸沮,可以開啟在
清除后合并壓縮
琳骡,但是整理階段,無法并發(fā)讼溺。導(dǎo)致停頓時間變長楣号,也可以設(shè)置多次Full GC
后壓縮碎片;
5. G1收集器(并發(fā)收集器)
標簽: 分代收集
空間整合
并發(fā)收集
可預(yù)測停頓
內(nèi)存化整為零
G1之前的其他收集器收集的范圍都是新生代
或者老年代
怒坯。G1將整個Java堆劃分為大小相等的獨立區(qū)域·(region [?ri:d??n])
炫狱。新生代
和老年代
不再是物理隔離的了。他們都是一部分的region
(不需要連續(xù))的集合剔猿。
G1收集器之所以能建立可預(yù)測的停頓時間模型视译。是因為G1可以跟蹤各個region
里面垃圾堆價值大小(回收所獲得的空間以及回收所需的時間經(jīng)驗值)归敬,在后臺維護了優(yōu)先列表酷含。根據(jù)允許的收集時間鄙早,優(yōu)先回收價值最大的Region
。(劃分空間以及有優(yōu)先級的區(qū)域回收椅亚。PS:G1執(zhí)行的第四步限番,篩選階段
,就是根據(jù)用戶期望GC時間呀舔,制定回收計劃0饴啤)
一個對象被分配到
region
中,他并非只能被region
中的其他對象引用别威,而是可以與整個java堆任意對象發(fā)生引用躯舔,那么可達性分析法判定對象是否存活時,豈不是還要掃描整個堆省古?在以前的分代收集中粥庄,新生代中的對象也面臨著相同的問題,Minor GC
時若是同時掃描老年代的話豺妓,那么Minor GC
效率可能下降不少惜互。
G1垃圾回收是否掃描整個堆,或者minor gc是否掃描整個堆琳拭?
在G1收集器中训堆,Region
之間的對象引用以及其他收集器新生代和老年代之間的對象引用。(敲黑板白嘁,劃重點)虛擬機都是使用Remembered Set
來避免全堆掃描的坑鱼。
G1中每個region
都有一個與之對應(yīng)的Remembered Set
,虛擬機發(fā)現(xiàn)程序在對Reference
類型的數(shù)據(jù)進行寫操作時絮缅,會產(chǎn)生一個Write Barrier
暫停暫時中斷寫操作鲁沥,檢查Reference
引用對象是否處于不同的Region
之中(分代中就是檢查是否老年代
中的對象引用新生代
的對象)。如果是耕魄,便通過CardTable
把相關(guān)的引用對象
所屬的Region
的Remembered Set
之中画恰,當進行垃圾回收時,在GC Roots
中的枚舉范圍中加入Remembered Set
即可保證不對全堆掃描也不會有遺漏吸奴。
小胖有話說:虛擬機對引用
類型數(shù)據(jù)
進行寫
操作時允扇,判斷引用的對象是否處于不同的Rigion
區(qū),或者是否是老年代對象
引用新生代對象
则奥,若是的話考润,則寫入Remembered Set
中,GC Roots
會在枚舉范圍加入Remembered Set
保證不進行全表掃描的情況下不會遺漏對象逞度。
(嘻嘻额划,以后面試官問G1執(zhí)行步驟時,將維護 Remembered Set
操作也可以描述一下呀)
G1收集器運作大致可劃分為以下幾個步驟:
初始標記(Initial Marking):暫停線程档泽,標記
GC Roots
能直接關(guān)聯(lián)到的對象俊戳。并發(fā)階段(Concurrent Marking):對堆中的對象進行可達性分析,耗時較長馆匿,可并發(fā)執(zhí)行抑胎;
最終階段(Final Marking):修正
并發(fā)階段
期間用戶程序運行導(dǎo)致標記產(chǎn)生變化的記錄;* (敲黑板渐北,繼續(xù)劃重點)*虛擬機將這段時間對象變化在線程Remembered Set Logs
中阿逃,最終階段需要把Remembered Set Logs
數(shù)據(jù)合并到Remembered Set
中,需要停頓線程赃蛛,但可并行執(zhí)行恃锉。篩選階段(Live Data Counting and Evacuation):根據(jù)用戶期望的GC停頓時間制定回收計劃。也可做到與用戶程序
并行執(zhí)行
呕臂。