垃圾收集算法
標(biāo)記-清除算法
算法分為“標(biāo)記”和“清除”階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。它是最基礎(chǔ)的收集算法陨囊,效率也很高蜘醋,但是會帶來兩個(gè)明顯的問題:
效率問題
空間問題(標(biāo)記清除后會產(chǎn)生大量不連續(xù)的碎片)
復(fù)制算法
為了解決效率問題压语,“復(fù)制”收集算法出現(xiàn)了胎食。它可以將內(nèi)存分為大小相同的兩塊厕怜,每次使用其中的一塊粥航。當(dāng)這一塊的內(nèi)存使用完后躁锡,就將還存活的對象復(fù)制到另一塊去置侍,然后再把使用的空間一次清理掉蜡坊。這樣就使每次的內(nèi)存回收都是對內(nèi)存區(qū)間的一半進(jìn)行回收秕衙。
標(biāo)記-整理算法
根據(jù)老年代的特點(diǎn)特出的一種標(biāo)記算法据忘,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣勇吊,但后續(xù)步驟不是直接對可回收對象回收汉规,而是讓所有存活的對象向一段移動,然后直接清理掉端邊界以外的內(nèi)存碟狞。
分代收集算法
當(dāng)前虛擬機(jī)的垃圾收集都采用分代收集算法族沃,這種算法沒有什么新的思想泌参,只是根據(jù)對象存活周期的不同將內(nèi)存分為幾塊及舍。一般將java堆分為新生代和老年代锯玛,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法攘残。
比如在新生代中歼郭,每次收集都會有大量對象死去病曾,所以可以選擇復(fù)制算法泰涂,只需要付出少量對象的復(fù)制成本就可以完成每次垃圾收集逼蒙。而老年代的對象存活幾率是比較高的是牢,而且沒有額外的空間對它進(jìn)行分配擔(dān)保驳棱,所以我們必須選擇“標(biāo)記-清除”或“標(biāo)記-整理”算法進(jìn)行垃圾收集艘策。
垃圾收集器
Serial收集器
(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial(串行)收集器收集器是最基本、歷史最悠久的垃圾收集器了却汉。大家看名字就知道這個(gè)收集器是一個(gè)單線程收集器了合砂。它的 “單線程” 的意義不僅僅意味著它只會使用一條垃圾收集線程去完成垃圾收集工作翩伪,更重要的是它在進(jìn)行垃圾收集工作的時(shí)候必須暫停其他所有的工作線程( "Stop The World" )缘屹,直到它收集結(jié)束轻姿。
新生代采用復(fù)制算法互亮,老年代采用標(biāo)記-整理算法豹休。
虛擬機(jī)的設(shè)計(jì)者們當(dāng)然知道Stop The World帶來的不良用戶體驗(yàn)威根,所以在后續(xù)的垃圾收集器設(shè)計(jì)中停頓時(shí)間在不斷縮短(仍然還有停頓屏积,尋找最優(yōu)秀的垃圾收集器的過程仍然在繼續(xù))炊林。
但是Serial收集器有沒有優(yōu)于其他垃圾收集器的地方呢渣聚?當(dāng)然有奕枝,它*簡單而高效(與其他收集器的單線程相比)*隘道。Serial收集器由于沒有線程交互的開銷,自然可以獲得很高的單線程收集效率忘晤。
ParNew收集器
ParNew收集器其實(shí)就是Serial收集器的多線程版本设塔,除了使用多線程進(jìn)行垃圾收集外闰蛔,其余行為(控制參數(shù)、收集算法任连、回收策略等等)和Serial收集器完全一樣课梳。
新生代采用復(fù)制算法,老年代采用標(biāo)記-整理算法爆土。
它是許多運(yùn)行在Server模式下的虛擬機(jī)的首要選擇步势,除了Serial收集器外背犯,只有它能與CMS收集器(真正意義上的并發(fā)收集器漠魏,后面會介紹到)配合工作柱锹。
并行和并發(fā)概念補(bǔ)充:
并行(Parallel) :指多條垃圾收集線程并行工作禁熏,但此時(shí)用戶線程仍然處于等待狀態(tài)瞧毙。適合科學(xué)計(jì)算寄症、后臺處理等弱交互場景有巧。
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時(shí)執(zhí)行(但不一定是并行剪决,可能會交替執(zhí)行)柑潦,用戶程序在繼續(xù)運(yùn)行渗鬼,而垃圾收集器運(yùn)行在另一個(gè)CPU上譬胎。適合Web應(yīng)用堰乔。
Parallel Scavenge收集器
(-XX:+UseParallelGC(新生代),-XX:+UseParallelOldGC(老生代)
Parallel Scavenge 收集器類似于ParNew 收集器镐侯,是Server 模式(內(nèi)存大于2G苟翻,2個(gè)cpu)下的默認(rèn)收集器崇猫,那么它有什么特別之處呢诅炉?
Parallel Scavenge收集器關(guān)注點(diǎn)是吞吐量(高效率的利用CPU)屋厘。CMS等垃圾收集器的關(guān)注點(diǎn)更多的是用戶線程的停頓時(shí)間(提高用戶體驗(yàn))擅这。所謂吞吐量就是CPU中用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值仲翎。Parallel Scavenge收集器提供了很多參數(shù)供用戶找到最合適的停頓時(shí)間或最大吞吐量,如果對于收集器運(yùn)作不太了解的話鲫构,可以選擇把內(nèi)存管理優(yōu)化交給虛擬機(jī)去完成也是一個(gè)不錯的選擇结笨。
新生代采用復(fù)制算法炕吸,老年代采用標(biāo)記-整理算法赫模。
Serial Old收集器
Serial收集器的老年代版本瀑罗,它同樣是一個(gè)單線程收集器。它主要有兩大用途:一種用途是在JDK1.5以及以前的版本中與Parallel Scavenge收集器搭配使用劣像,另一種用途是作為CMS收集器的后備方案驾讲。
CMS收集器
(-XX:+UseConcMarkSweepGC(old) -XX:+UseParNewGC)
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。它實(shí)現(xiàn)了讓垃圾收集線程與用戶線程(基本上)同時(shí)工作颅停。
步驟
CMS收集器是一種 “標(biāo)記-清除”算法實(shí)現(xiàn)癞揉。整個(gè)過程分為四個(gè)步驟:
- 初始標(biāo)記: 暫停所有的其他線程(STW)喊熟,并記錄下直接與root相連的對象芥牌,速度很快 ;
- 并發(fā)標(biāo)記: 同時(shí)開啟GC和用戶線程壁拉,用一個(gè)閉包結(jié)構(gòu)去記錄可達(dá)對象。但在這個(gè)階段結(jié)束溃论,這個(gè)閉包結(jié)構(gòu)并不能保證包含當(dāng)前所有的可達(dá)對象钥勋。因?yàn)橛脩艟€程可能會不斷的更新引用域辆苔,所以GC線程無法保證可達(dá)性分析的實(shí)時(shí)性姑子。所以這個(gè)算法里會跟蹤記錄這些發(fā)生引用更新的地方街佑。
- 重新標(biāo)記: 重新標(biāo)記階段就是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄沐旨,這個(gè)階段的停頓時(shí)間一般會比初始標(biāo)記階段的時(shí)間稍長磁携,遠(yuǎn)遠(yuǎn)比并發(fā)標(biāo)記階段時(shí)間短;
- *并發(fā)清除: 開啟用戶線程,同時(shí)GC線程開始對未標(biāo)記的區(qū)域做清掃闷供。
缺點(diǎn)
對CPU資源敏感(會和服務(wù)搶資源)歪脏;
無法處理浮動垃圾(在java業(yè)務(wù)程序線程與垃圾收集線程并發(fā)執(zhí)行過程中又產(chǎn)生的垃圾粮呢,這種浮動垃圾只能等到下一次gc再清理了)啄寡;
它使用的回收算法-“標(biāo)記-清除”算法會導(dǎo)致收集結(jié)束時(shí)會有大量空間碎片產(chǎn)生挺物。
相關(guān)參數(shù)
-XX:+UseCMSCompactAtFullCollection:FullGC之后做壓縮(減少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后壓縮一次(因壓縮非常的消耗時(shí)間嵌溢,所以不能每次FullGC都做)
-XX:CMSInitiatingOccupancyFraction:觸發(fā)FulGC條件(默認(rèn)是92)
-XX:+UseCMSInitiatingOccupancyOnly:是否動態(tài)調(diào)節(jié)
-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC(一般這個(gè)參數(shù)是打開的)
-XX:+CMSClassUnloadingEnabled:啟用回收Perm區(qū)(jdk1.7及以前)
G1收集器
(-XX:+UseG1GC)
G1 (Garbage-First)是一款面向服務(wù)器的垃圾收集器,主要針對配備多顆處理器及大容量內(nèi)存的機(jī)器. 以極高概率滿足GC停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征.
G1將Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)赖草,雖保留新生代和老年代的概念秧骑,但不再是物理隔閡了,它們都是(可以不連續(xù))Region的集合绒疗。
分配大對象(直接進(jìn)Humongous區(qū)吓蘑,專門存放短期巨型對象坟冲,不用直接進(jìn)老年代健提,避免Full GC的大量開銷)不會因?yàn)闊o法找到連續(xù)空間而提前觸發(fā)下一次GC私痹。
特點(diǎn)
并行與并發(fā):G1能充分利用CPU紊遵、多核環(huán)境下的硬件優(yōu)勢,使用多個(gè)CPU(CPU或者CPU核心)來縮短Stop-The-World停頓時(shí)間匀奏。部分其他收集器原本需要停頓Java線程來執(zhí)行GC動作,G1收集器仍然可以通過并發(fā)的方式讓java程序繼續(xù)執(zhí)行恒水。
分代收集:雖然G1可以不需要其他收集器配合就能獨(dú)立管理整個(gè)GC堆饲齐,但是還是保留了分代的概念捂人。
空間整合:與CMS的“標(biāo)記--清理”算法不同,G1從整體來看是基于“*標(biāo)記整理*”算法實(shí)現(xiàn)的收集器酸纲;從局部上來看是基于“復(fù)制”算法實(shí)現(xiàn)的闽坡。
-
可預(yù)測的停頓: 這是G1相對于CMS的另一個(gè)大優(yōu)勢,降低停頓時(shí)間是G1 和 CMS 共同的關(guān)注點(diǎn)外厂,但G1 除了追求低停頓外汁蝶,還能建立可預(yù)測的停頓時(shí)間模型掖棉,能讓使用者明確指定在一個(gè)長度為M毫秒的時(shí)間片段內(nèi)完成垃圾收集啊片。
G1收集器的運(yùn)作大致分為以下幾個(gè)步驟:
步驟
- 初始標(biāo)記(initial mark玖像,STW):在此階段,G1 GC 對根進(jìn)行標(biāo)記笤昨。該階段與常規(guī)的 (STW) 年輕代垃圾回收密切相關(guān)握恳。
- 并發(fā)標(biāo)記(Concurrent Marking):G1 GC 在整個(gè)堆中查找可訪問的(存活的)對象崇裁。
- 最終標(biāo)記(Remark拔稳,STW):該階段是 STW 回收锹雏,幫助完成標(biāo)記周期。
- 篩選回收(Cleanup采记,STW):篩選回收階段首先對各個(gè)Region的回收價(jià)值和成本進(jìn)行排序唧龄,*根據(jù)用戶所期望的GC停頓時(shí)間來制定回收計(jì)劃*选侨,這個(gè)階段其實(shí)也可以做到與用戶程序一起并發(fā)執(zhí)行援制,但是因?yàn)橹换厥找徊糠諶egion芍瑞,時(shí)間是用戶可控制的洪己,而且停頓用戶線程將大幅提高收集效率答捕。
G1垃圾收集分類
YoungGC
新對象進(jìn)入Eden區(qū)
存活對象拷貝到Survivor區(qū)
存活時(shí)間達(dá)到年齡閾值時(shí)屑那,對象晉升到Old區(qū)
MixedGC
不是FullGC沃琅,回收所有的Young和部分Old(根據(jù)期望的GC停頓時(shí)間確定old區(qū)垃圾收集的優(yōu)先順序)
global concurrent marking (全局并發(fā)標(biāo)記)
Initial marking phase:標(biāo)記GC Root蜘欲,STW
Root region scanning phase:標(biāo)記存活Region
Concurrent marking phase:標(biāo)記存活的對象
Remark phase :重新標(biāo)記,STW
Cleanup phase:部分STW
相關(guān)參數(shù)
G1MixedGCLiveThresholdPercent Old區(qū)的region被回收的時(shí)候的存活對象占比
G1MixedGCCountTarget:一次global concurrent marking之后郭脂,最多執(zhí)行Mixed GC的次數(shù)
G1OldCSetRegionThresholdPercent 一次Mixed GC中能被選入CSet的最多old區(qū)的region數(shù)量
觸發(fā)的時(shí)機(jī)
InitiatingHeapOccupancyPercent:堆占有率達(dá)到這個(gè)值則觸發(fā)global concurrent marking,默認(rèn)45%
G1HeapWastePercent:在global concurrent marking結(jié)束之后展鸡,可以知道區(qū)有多少空間要被回收娱颊,在每次YGC之后和再次發(fā)生Mixed GC之前凯砍,會檢查垃圾占比是否達(dá)到了此參數(shù)悟衩,只有達(dá)到了惠昔,下次才會發(fā)生Mixed GC
如何選擇垃圾收集器
優(yōu)先調(diào)整堆的大小讓服務(wù)器自己來選擇
如果內(nèi)存小于100M镇防,使用串行收集器
如果是單核来氧,并且沒有停頓時(shí)間的要求啦扬,串行或JVM自己選擇
如果允許停頓時(shí)間超過1秒凫碌,選擇并行或者JVM自己選
如果響應(yīng)時(shí)間最重要扑毡,并且不能超過1秒,使用并發(fā)收集器
官方推薦使用G1盛险,因?yàn)樾阅芨?/strong>
調(diào)優(yōu)
JVM調(diào)優(yōu)主要就是調(diào)整下面兩個(gè)指標(biāo)
停頓時(shí)間: 垃圾收集器做垃圾回收中斷應(yīng)用執(zhí)行的時(shí)間瞄摊。 -XX:MaxGCPauseMillis
吞吐量:垃圾收集的時(shí)間和總時(shí)間的占比:1/(1+n),吞吐量為1-1/(1+n) 枉层。
-XX:GCTimeRatio=n
GC調(diào)優(yōu)步驟
打印GC日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -
分析工具:gceasy泉褐,GCViewer
G1調(diào)優(yōu)目標(biāo)
6GB以上內(nèi)存
停頓時(shí)間是500ms以內(nèi)
吞吐量是90%以上
GC常用參數(shù)
堆棧設(shè)置
-Xss:每個(gè)線程的棧大小
-Xms:初始堆大小,默認(rèn)物理內(nèi)存的1/64
-Xmx:最大堆大小鸟蜡,默認(rèn)物理內(nèi)存的1/4
-Xmn:新生代大小
-XX:NewSize:設(shè)置新生代初始大小
-XX:NewRatio:默認(rèn)2表示新生代占年老代的1/2膜赃,占整個(gè)堆內(nèi)存的1/3。
-XX:SurvivorRatio:默認(rèn)8表示一個(gè)survivor區(qū)占用1/8的Eden內(nèi)存揉忘,即1/10的新生代內(nèi)存疲眷。
-XX:MetaspaceSize:設(shè)置元空間大小
-XX:MaxMetaspaceSize:設(shè)置元空間最大允許大小,默認(rèn)不受限制,JVM Metaspace會進(jìn)行動態(tài)擴(kuò)展县习。
垃圾回收統(tǒng)計(jì)信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
收集器設(shè)置
-XX:+UseSerialGC:設(shè)置串行收集器
-XX:+UseParallelGC:設(shè)置并行收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParalledlOldGC:設(shè)置并行老年代收集器
-XX:+UseConcMarkSweepGC:設(shè)置CMS并發(fā)收集器
-XX:+UseG1GC:設(shè)置G1收集器
-XX:ParallelGCThreads:設(shè)置用于垃圾回收的線程數(shù)
并行收集器設(shè)置
-XX:ParallelGCThreads:設(shè)置并行收集器收集時(shí)使用的CPU數(shù)彤钟。并行收集線程數(shù)峡眶。
-XX:MaxGCPauseMillis:設(shè)置并行收集最大暫停時(shí)間
-XX:GCTimeRatio:設(shè)置垃圾回收時(shí)間占程序運(yùn)行時(shí)間的百分比。公式為1/(1+n)
-XX:YoungGenerationSizeIncrement:年輕代gc后擴(kuò)容比例,默認(rèn)是20(%)**
CMS收集器設(shè)置
-XX:+UseConcMarkSweepGC:設(shè)置CMS并發(fā)收集器
-XX:+CMSIncrementalMode:設(shè)置為增量模式。適用于單CPU情況。
-XX:ParallelGCThreads:設(shè)置并發(fā)收集器新生代收集方式為并行收集時(shí),使用的CPU數(shù)。并行收集線程數(shù)堪遂。
-XX:CMSFullGCsBeforeCompaction:設(shè)定進(jìn)行多少次CMS垃圾回收后,進(jìn)行一次內(nèi)存壓縮
-XX:+CMSClassUnloadingEnabled:允許對類元數(shù)據(jù)進(jìn)行回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到達(dá)閥值的時(shí)候捏境,才進(jìn)行CMS回收
-XX:+CMSIncrementalMode:設(shè)置為增量模式倾剿。適用于單CPU情況
-XX:ParallelCMSThreads:設(shè)定CMS的線程數(shù)量
-XX:CMSInitiatingOccupancyFraction:設(shè)置CMS收集器在老年代空間被使用多少后觸發(fā)
-XX:+UseCMSCompactAtFullCollection:設(shè)置CMS收集器在完成垃圾收集后是否要進(jìn)行一次內(nèi)存碎片的整理
G1收集器設(shè)置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的線程數(shù)量
-XX:G1HeapRegionSize:指定分區(qū)大小(1MB~32MB凛捏,且必須是2的冪)示罗,默認(rèn)將整堆劃分為2048個(gè)分區(qū)
***\*-XX:GCTimeRatio\****:吞吐量大小拌阴,0-100的整數(shù)(默認(rèn)9)脯倒,值為n則系統(tǒng)將花費(fèi)不超過1/(1+n)的時(shí)間用于垃圾收集
***\*-XX:MaxGCPauseMillis\****:目標(biāo)暫停時(shí)間(默認(rèn)200ms)
-XX:G1NewSizePercent:新生代內(nèi)存初始空間(默認(rèn)整堆5%)
-XX:G1MaxNewSizePercent:新生代內(nèi)存最大空間
-XX:TargetSurvivorRatio:Survivor填充容量(默認(rèn)50%)
-XX:MaxTenuringThreshold:最大任期閾值(默認(rèn)15)
***\*-XX:InitiatingHeapOccupancyPercen\****:老年代占用空間超過整堆比IHOP閾值(默認(rèn)45%),超過則執(zhí)行混合收集
***\*-XX:G1HeapWastePercent\****:堆廢物百分比(默認(rèn)5%)
-XX:G1MixedGCCountTarget:參數(shù)混合周期的最大總次數(shù)(默認(rèn)8)