目錄
一枣察、概述
二、如何判斷對象是否為垃圾
三太防、回收算法
四辞嗡、垃圾收集器
一、概述
- GC內(nèi)存回收的地方
虛擬機棧,本地方法棧,隨著線程而生,線程而滅,虛擬機棧中的棧幀分配多少內(nèi)存已經(jīng)在類結(jié)構(gòu)就確定了,還有這幾個區(qū)域的內(nèi)存和回收都具有確定性,不需要過多的考慮回收的問題,主要回收的指的是java堆內(nèi)存 - Minor GC,Major GC,Full GC
- minorGC 是清理整合YouGen的過程氓皱, eden 的清理褒翰,S0\S1的清理都由于MinorGC
- Major GC 是清理OldGen。
- Full GC 是清理整個堆空間—包括年輕代和永久代匀泊。
- 執(zhí)行速度
Minor GC 執(zhí)行快 (50 ms內(nèi)).
Minor GC 執(zhí)行不頻繁 (大概10s 執(zhí)行一次).
Full GC 執(zhí)行快 (1 second內(nèi)).
Full GC 執(zhí)行不頻繁 (10 minutes 一次). - YGC和FGC
1优训、YGC和FGC是什么
YGC :對新生代堆進行g(shù)c。頻率比較高各聘,因為大部分對象的存活壽命較短揣非,在新生代里被回收。性能耗費較小躲因。 (復(fù)制算法 ---> 一般適用對象存活率低的場景)
FGC :全堆范圍的gc早敬。默認(rèn)堆空間使用到達80%(可調(diào)整)的時候會觸發(fā)fgc忌傻。(標(biāo)記整理或者標(biāo)記清除算法 ---> 一般適用于對象存活率高的場景)
2、什么時候執(zhí)行YGC和FGC
(1)eden空間不足,執(zhí)行 young gc
(2)old空間不足搞监,perm空間不足水孩,調(diào)用方法System.gc() ,ygc時的悲觀策略, dump live的內(nèi)存信息時(jmap –dump:live表示我們需要抓取目前在生命周期內(nèi)的內(nèi)存對象)琐驴,都會執(zhí)行full gc
二俘种、如何判斷對象是否為垃圾
四種引用(強, 軟,弱,虛)
// 1.強引用
String strongReference = new String("abc");
strongReference = null
// 2.軟引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<String>(str);
if(JVM內(nèi)存不足) {
// 將軟引用中的對象引用置為null
str = null;
// 通知垃圾回收器進行回收
System.gc();
}
// 3.弱引用
String str=newString("abc");
WeakReference<String> WeakRef = new WeakReference<String>(str);
str=null;
//弱引用 ->變--> 強引用
String abc = WeakRef.get();
- 強引用:
- Object strongReference = new Object();
-
內(nèi)存空間不足時,
Java
虛擬機寧愿拋出OutOfMemoryError
錯誤 - 強引用對象不適用需要回收時,需要弱化 strongReference = null才能回收
- 軟引用(瀏覽器高速緩存):
- 如果內(nèi)存空間不足了绝淡,就會回收這些對象的內(nèi)存宙刘。
-
GC會在虛擬機拋出
OutOfMemoryError
之前回收軟引用對象 - 優(yōu)先回收長時間閑置不用的軟引用對象,較新的會保留
- 弱引用(ThreadLocal的Node節(jié)點的key):
- 每次回收必回收掉的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存牢酵。
- GC是個優(yōu)先級很低的線程,不一定很快發(fā)現(xiàn)弱引用的對象
- 虛引用(管理堆外內(nèi)存):
- 一個對象僅持有虛引用悬包,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收馍乙。
- 主要用來跟蹤對象被垃圾回收器回收的活動
1.引用計算法
public class quote_calc_method {
/**
* VM args
* -verbose:gc -XX:+PrintGCDetails
* @param args
*/
public Object object;
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[10 * _1MB];
public static void main(String[] args) {
quote_calc_method a1 = new quote_calc_method();
quote_calc_method a2 = new quote_calc_method();
//在堆內(nèi)存a1,a2中相互指向(相互引用)
a1.object = a2;
a2.object = a1;
//棧指向堆內(nèi)存的引用刪除
a1 = null;
a2 = null;
//結(jié)果:對象之間的循環(huán)引用問題,計算器!=1,但是卻屬于垃圾
//在這里發(fā)生GC,a1和a2對象能否被回收,
//如果是引用計算法,則不會回收,因為兩個對象還存在彼此的引用
//如果回收了,說明采用了別的方法
System.gc();
}
}
- 定義:給對象添加一個引用計數(shù)器,當(dāng)有一個地方引用它時,計數(shù)器加一,引用失效時,值減一,計數(shù)器為0時的對象就是不可能再被使用的
- 優(yōu)點:實現(xiàn)簡單,判斷效率高
- 缺點:難以解決對象內(nèi)部相互循環(huán)引用的問題
2.可達性分析
JVM就是通過可達性分析判斷哪些對象需要被回收
定義:通過一些列被稱為"GC Roots"的對象作為起始點,從這些結(jié)點向下搜索,搜索走過的路徑稱為引用鏈,當(dāng)一個對象到GC Roots沒有任何的引用鏈相連時,此對象不可用的布近。
-
可作為GC Roots的對象是
- 虛擬機棧引用的對象
- 方法區(qū)中類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- 本地方法棧(Native方法)引用的對象
-
例子:如下圖所示,object1~object4對GC Root都是可達的丝格,說明不可被回收吊输,object5和object6對GC Root節(jié)點不可達,說明其可以被回收铁追。
image-20200725074321593.png
三季蚂、回收算法
1.標(biāo)記-清除算法
- 定義:
最基礎(chǔ)的收集算法,通過可達性分析法標(biāo)記所有需要回收的對象,在標(biāo)記后統(tǒng)一回收所有被標(biāo)記的對象 - 缺點:
- 效率問題:標(biāo)記和清除的效率都不高
- 空間問題:標(biāo)記后產(chǎn)生大量的內(nèi)存碎片(不連續(xù)的內(nèi)存空間),在分配較大對象內(nèi)存時,可能無法找到足夠的連續(xù)內(nèi)存空間不得不提前觸發(fā)另一次的垃圾收集動作
-
圖解
image-20200725075408981.png
2.復(fù)制算法
- 定義:為了解決效率問題,復(fù)制收集算法出現(xiàn)了,將內(nèi)存劃分大小相等的兩塊,每次只使用其中一塊,當(dāng)這塊內(nèi)存用完了,將存活的對象復(fù)制到另外一塊內(nèi)存上,然后把使用過的內(nèi)存空間一次清理掉。
- 優(yōu)點:實現(xiàn)簡單,運行高效
- 缺點:內(nèi)存縮減為原來的一般,對于存活較多的對象,頻繁的進行復(fù)制操作,效率降低且需要內(nèi)存分配擔(dān)保
-
效果圖
image-20200725084052636.png
衍生版本:
- 過程:
1.劃分為Eden(伊甸園)和Survivor區(qū)琅束,最后Survivor由FromSpace和ToSpace組成,三個區(qū)域默認(rèn)情況下是按照8:1:1分配,也可以手動配置扭屁。
2.JVM每次只會使用Eden區(qū)和一塊Survivor區(qū)來為對象服務(wù),另一塊Survivor區(qū)域是空的,用于垃圾回收
3.舉例:
第一次GC,將Eden+from存活的對象復(fù)制到to,清空Eden和from(存活對象小于to的空間)
第二次GC,虛擬機使用Eden+to,將存活對象移到from
就這樣反復(fù)在from,to循環(huán)
4.如果Eden區(qū)域未回收的內(nèi)存超過from或者to的自身空間,則"內(nèi)存擔(dān)保",由from或者to空間移到Tenured Gen老年代中 - 優(yōu)點:復(fù)制算法只復(fù)制了10%的內(nèi)存空間,空間的損耗達到可接受范圍,解決了內(nèi)存碎片(空間問題)
3.標(biāo)記-整理算法
- 定義:與標(biāo)記-清除算法比,不同在于標(biāo)記清除只是把可回收的對象進行垃圾回收,不會對剩余內(nèi)存進行整理,整理的過程是將可用內(nèi)存往一端移動,把回收對象往另一端移動,最后將回收對象部分內(nèi)存進行回收
- 優(yōu)點:解決了標(biāo)記-清除算法的內(nèi)存碎片問題,也解決了復(fù)制算法的空間利用率(內(nèi)存浪費)問題
- 缺點:來回移動整理導(dǎo)致比前兩者效率低
-
效果圖
image-20200725085821318.png
4.分代收集算法
定義:針對不同的分代區(qū),選擇不同的垃圾收集算法(2.復(fù)制算法 3.標(biāo)記-整理算法)涩禀。
復(fù)制算法:針對回收率較高的區(qū)域,只需要復(fù)制少量的存活對象----新生代
標(biāo)記-清除算法/標(biāo)記-整理算法:針對回收率較低的區(qū)域,只需要清除少量的垃圾對象----老年代
四料滥、垃圾收集器
1.Serial
定義:
單線程的新生代垃圾收集器(復(fù)制算法),單線程:只會用一條收集線程去完成垃圾收集工作,但是它必須暫停所有的用戶線程,直到它完成垃圾收集工作(stop the world STW)----其他所有線程都被掛起(除了垃圾收集幫助器之外)安全點(Safe Point):
VM Threads,專門用來執(zhí)行一些特殊的VM Operation艾船,比如分派GC葵腹,thread dump等,這些任務(wù)屿岂,都需要整個Heap践宴,以及所有線程的狀態(tài)是靜止的,一致的才能進行爷怀。所以JVM引入了安全點(Safe Point)的概念阻肩,想辦法在需要進行VM Operation時,通知所有的線程進入一個靜止的安全點运授。-
Serial Old收集器
- Serial收集器的老年代版本,用"用標(biāo)記-整理"算法的單線程收集器
- 用途
- 與Parallel Scavenge收集器搭配使用
- 作為CMS(針對老年代的多線程)收集器的后備方案,在并發(fā)收集算法Concurrent Mode Failure的搭配使用
- CMS一般和Parnew搭配使用,不能和Parallel搭配
-
效果圖
image-20200725095052530.png
2.Parnew
- 定義:多線程版本的Serial收集器,許多Server服務(wù)器的首選新生代收集器(復(fù)制算法)
- 配合:
- Serial配合CMS收集器
- Parnew配合CMS收集器
3.Parallel Scavenge
- 定義:多線程的新生代收集器(復(fù)制算法)
達到可控制的吞吐量
其他收集器的關(guān)注點:盡量的縮短GC時用戶線程的停頓時間 - 與Parnew比較
- 可以達到可控制的吞吐量
- Parnew配合CMS收集器,Parallel不能
- 參數(shù)
-XX:MaxGCPauseMillis 垃圾回收器最大的停頓時間
-XX:GCTimeRatio (0,100) 吞吐量大小
99代表100分鐘的cpu占用時間,GC時間為1分鐘
- 吞吐量計算
CPU用于用戶代碼的時間與CPU總消耗時間的比值
即吞吐量=運行用戶代碼時間1 (運行用戶代碼時間+垃圾收集時間)
虛擬機總共運行了100 分鐘烤惊,其中垃圾收集花掉1分鐘乔煞,那吞吐量就是99% - Parallel Old收集器
- 是Parallel Scavenge的老年代版本,多線程和使用"標(biāo)記-整理"算法
-
在注重吞吐量和CPU的敏感場合,可以優(yōu)先考慮Parallel Scavenge和Parallel Old的組合
image-20200725104030716.png
4.CMS(Concurrent Mark Sweep)
并發(fā)和并行的區(qū)別
并行(parallel):指在同一時刻,有多條指令在多個處理器上同時執(zhí)行柒室。所以無論從微觀還是從宏觀來看渡贾,二者都是一起執(zhí)行的。
并發(fā)(concurrency):指在同一時刻只能有一條指令執(zhí)行雄右,但多個進程指令被快速的輪換執(zhí)行空骚,使得在宏觀上具有多個進程同時執(zhí)行的效果,但在微觀上并不是同時執(zhí)行的不脯,只是把時間分成若干段府怯,使多個進程快速交替的執(zhí)行刻诊。并發(fā)和并行的區(qū)別
定義:是以一種最短回收停頓時間(最小響應(yīng)時間)為目的的垃圾收集器
使用場景:重視服務(wù)的響應(yīng)速度和系統(tǒng)停頓時間,JavaWeb項目
-
CMS的并發(fā)可能指的是并行???
在GC領(lǐng)域: concurrent算法指GC線程和業(yè)務(wù)線程能并發(fā)執(zhí)行防楷; parallel指GC線程之間的并行。
-
收集四個步驟
- 初始標(biāo)記(initial mark): 標(biāo)記GC Roots可以關(guān)聯(lián)到的對象,需要STW,速度快
- 并發(fā)標(biāo)記(concurrent marl)--耗時長且同時執(zhí)行: 從"初始標(biāo)記"開始找出存活對象,標(biāo)記比較好使,GC和用戶線程可以同時執(zhí)行,
- 重新標(biāo)記(remark): 修正在"并發(fā)標(biāo)記"期間因用戶線程繼續(xù)執(zhí)行產(chǎn)生變動的那一部分對象的標(biāo)記.需要STW,標(biāo)記后,Java堆中存活的對象已保證都被標(biāo)記
- 并發(fā)清除(concurrent sweep)--耗時長且同時執(zhí)行: 釋放年老代的垃圾對象,用戶線程可以同時執(zhí)行
-
優(yōu)點
- "并發(fā)標(biāo)記和并發(fā)清除",并發(fā)標(biāo)記,并發(fā)清除耗時長,但是都可以與用戶線程同時執(zhí)行,總體上,CMS的內(nèi)存回收時與用戶線程同時執(zhí)行的
- 低停頓
-
缺點
CMS收集器對CPU資源非常敏感,CMS默認(rèn)啟動的回收線程數(shù)是(CPU數(shù)+3)/4则涯,當(dāng)CPU不足4個時复局,CMS對用戶程序的影響就可能變得很大。
CMS無法處理浮動垃圾粟判,就可能出現(xiàn)“Concurrent Mode Failure”而導(dǎo)致另一次Full GC的產(chǎn)生
因為在并行清除的時用戶線程會同時執(zhí)行,所產(chǎn)生的垃圾稱為"浮動垃圾",CMS無法在當(dāng)次收集中處理它們,所有CMS需要預(yù)留一部分空間給并發(fā)收集時用戶線程的使用(這里不會STW),萬一預(yù)留的內(nèi)存不滿足程序需要,就會出現(xiàn)一次"Concurrent Mode Failure",這時JVM臨時啟動Serial Old來重新進行老年代的垃圾收集(STW),停頓時間更長空間碎片,因為"標(biāo)記-清除"算法,并發(fā)清除產(chǎn)生大量的空間碎片,即使老年代很多剩余空間,但無法找到連續(xù)空間來分配大對象,導(dǎo)致不得不觸發(fā)Full GC
參數(shù)
-XX:CMSInitiatingOccupancyFraction設(shè)置太高導(dǎo)致 Concurrent Mode Failure -XX:CMSCompactAtFullCollection
FullGC贈送碎片整理
-XX:CMSFullGCsBeforeCompaction ````````` 執(zhí)行多少次不壓縮的FullGC,來一次壓縮的FullGC-
效果圖
image-20200725115743553.png
5.G1
Garbage First
- 定義:是一個并行并發(fā)的增量式壓縮(內(nèi)存連續(xù)->指針碰撞方式分配內(nèi)存)|低停頓,用于多核大內(nèi)存的服務(wù)器垃圾收集器
- 特點:
- 并行和并發(fā),G1能利用多CPU,多核的硬件優(yōu)勢,來縮短STW的停頓時間
- 分代收集,分代概念仍然保留,但G1采取不同的方式去處理對象以獲得更好的收集效果
- 空間整合,G1從整體看-------"標(biāo)記-整合",從局部看-------"復(fù)制算法"
- 可預(yù)測的停頓,讓使用者指定在一個長度M毫秒的時間,消耗在垃圾收集上的時間不超過N毫秒
- Region出現(xiàn):G1收集器的Java堆內(nèi)存的分布于其他的收集器不同,劃分了大小相等的獨立區(qū)域(Region),而且老年代跟新生代不在是物理隔離了
參數(shù):
-XX:G1HeapRegionSize,大小只能為2的冪次方(MB)---------Region的大小
默認(rèn)的Region的數(shù)量為2048個 - 收集步驟
- 初始標(biāo)記:標(biāo)記了從GC Root可達的對象,需要STW(stop the world)
- 并發(fā)標(biāo)記:從GC Root開始可達性分析,找出存活的對象,耗時較長,但是可與用戶線程同時執(zhí)行,JVM將變化記錄在Remember Set Logs
- 最終標(biāo)記(并發(fā)):需要STW,為了修改在"并發(fā)標(biāo)記"期間因用戶線程繼續(xù)執(zhí)行所導(dǎo)致一部分標(biāo)識改變的對象,將Remember Set Logs的數(shù)據(jù)合并到Remember Set
- 篩選回收(并發(fā)):需要STW,首先對各個Region的回收價值和成本進行排序,根據(jù)用戶期望的GC停頓時間來指定回收計劃,暫停用戶線程,但是是并發(fā)回收的亿昏,所以速度快,而且回收效率高
-
效果圖
image-20200725155703031.png
- CMS和G1的不同點
- CMS的并發(fā)回收是與用戶線程一起進行的档礁,不需要停頓角钩。如果有垃圾是在標(biāo)記過程后產(chǎn)生的,那么就會產(chǎn)生一個回收不徹底的問題,設(shè)想一下一邊打掃房間呻澜,一邊有人在那扔垃圾递礼,那么最后的打掃效果是不是不好?而G1的篩選回收是需要暫停用戶線程羹幸,但是是并發(fā)回收的脊髓,所以速度快,而且回收效率高栅受。
- CMS的回收器不可以對young generation回收将硝,只能對old generation回收。因此需要與其他收集器配合屏镊,比如ParNew或者Serial依疼。而G1無論是young還是old都可以回收,人家可以獨立管理整個java堆而芥。而且回收算法是采用的標(biāo)記整理(局部是復(fù)制清除算法涛贯,因為young generation中采用復(fù)制清除效率更高),不會產(chǎn)生內(nèi)存碎片的問題蔚出。對比CMS只是可憐的標(biāo)記清除弟翘,在一些高qps場景的服務(wù)中虫腋,內(nèi)存碎片化很嚴(yán)重,很容易造成Full Gc稀余!
- java堆在使用這兩款回收器的時候悦冀,內(nèi)存布局不同。在使用CMS收集器時睛琳,新生代和老年代是物理隔離的(為兩個不同的連續(xù)的內(nèi)存區(qū)域)盒蟆。而用G1是對整個Heap劃分成若干個不同的Region,新生代和老年代是相互交叉的內(nèi)存區(qū)域师骗,是邏輯上的分類历等。這樣劃分的目的是規(guī)避每次在對象進行可達性分析都要在堆的全區(qū)域中進行。比如一個Collection Set中的每個region都對應(yīng)著一個Remembered set辟癌,G1可以維護這個Remembered Set寒屯,從中挑選出最具回收性價比的region進行回收,G1嘛黍少,就是Grabage First寡夹,提高回收效率。
- Remember Set和Card Table
RS(Remember Set)是一種抽象概念厂置,用于記錄從非收集部分指向收集部分的指針的集合菩掏。 在傳統(tǒng)的分代垃圾回收算法里面,RS(Remember Set)被用來記錄分代之間的指針昵济。在G1回收器里面智绸,RS被用來記錄從其他Region指向一個Region的指針情況。因此访忿,一個Region就會有一個RS瞧栗。這種記錄可以帶來一個極大的好處:在回收一個Region的時候不需要執(zhí)行全堆掃描,只需要檢查它的RS就可以找到外部引用醉顽,而這些引用就是initial mark的根之一沼溜。
那么,如果一個線程修改了Region內(nèi)部的引用游添,就必須要去通知RS系草,更改其中的記錄。為了達到這種目的唆涝,G1回收器引入了一種新的結(jié)構(gòu)找都,CT(Card Table)——卡表。每一個Region廊酣,又被分成了固定大小的若干張卡(Card)能耻。每一張卡,都用一個Byte來記錄是否修改過∠停卡表即這些byte的集合饿幅。實際上,如果把RS理解成一個概念模型戒职,那么CT就可以說是RS的一種實現(xiàn)方式栗恩。
-
G1內(nèi)存結(jié)構(gòu)
image-20200725162205657.png