1.什么是GC?
大白話說就是垃圾回收機(jī)制,內(nèi)存空間是有限的,你創(chuàng)建的每個(gè)對(duì)象和變量都會(huì)占據(jù)內(nèi)存,gc做的就是對(duì)象清除將內(nèi)存釋放出來,這就是GC要做的事。
2.需要GC的區(qū)域
說起垃圾回收的場(chǎng)所,了解過JVM(Java Virtual Machine Model)內(nèi)存模型的朋友應(yīng)該會(huì)很清楚,堆是Java虛擬機(jī)進(jìn)行垃圾回收的主要場(chǎng)所,其次要場(chǎng)所是方法區(qū)。
3.堆內(nèi)存的結(jié)構(gòu)(1.7)
在JDK1.8之后,堆的永久區(qū)取消了
由元空間取代
Java將堆內(nèi)存分為3大部分:新生代、老年代和永久代,其中新生代又進(jìn)一步劃分為Eden、S0、S1(Survivor)三個(gè)區(qū)

4.堆內(nèi)存上對(duì)象的分配與回收:
我們創(chuàng)建的對(duì)象會(huì)優(yōu)先在Eden分配,如果是大對(duì)象(很長(zhǎng)的字符串?dāng)?shù)組)則可以直接進(jìn)入老年代。虛擬機(jī)提供一個(gè)
-XX:PretenureSizeThreadhold參數(shù),令大于這個(gè)參數(shù)值的對(duì)象直接在老年代中分配,避免在Eden區(qū)和兩個(gè)Survivor區(qū)發(fā)生大量的內(nèi)存拷貝。
另外,長(zhǎng)期存活的對(duì)象將進(jìn)入老年代,每一次MinorGC(年輕代GC),對(duì)象年齡就大一歲,默認(rèn)15歲晉升到老年代,通過
-XX:MaxTenuringThreshold設(shè)置晉升年齡。
堆內(nèi)存上的對(duì)象回收也叫做垃圾回收,那么垃圾回收什么時(shí)候開始呢?
垃圾回收主要是完成清理對(duì)象,整理內(nèi)存的工作。上面說到GC經(jīng)常發(fā)生的區(qū)域是堆區(qū),堆區(qū)還可以細(xì)分為新生代、老年代。新生代還分為一個(gè)Eden區(qū)和兩個(gè)Survivor區(qū)。垃圾回收分為年輕代區(qū)域發(fā)生的Minor GC和老年代區(qū)域發(fā)生的Full GC,分別介紹如下。
Minor GC(年輕代GC):
對(duì)象優(yōu)先在Eden中分配,當(dāng)Eden中沒有足夠空間時(shí),虛擬機(jī)將發(fā)生一次Minor GC,因?yàn)镴ava大多數(shù)對(duì)象都是朝生夕滅,所以Minor GC非常頻繁,而且速度也很快。
Full GC(老年代GC):
Full GC是指發(fā)生在老年代的GC,當(dāng)老年代沒有足夠的空間時(shí)即發(fā)生Full GC,發(fā)生Full GC一般都會(huì)有一次Minor GC。
動(dòng)態(tài)對(duì)象年齡判定:
如果Survivor空間中相同年齡所有對(duì)象的大小總和大于Survivor空間的一半,那么年齡大于等于該對(duì)象年齡的對(duì)象即可晉升到老年代,不必要等到-XX:MaxTenuringThreshold。
空間分配擔(dān)保:
發(fā)生Minor GC時(shí),虛擬機(jī)會(huì)檢測(cè)之前每次晉升到老年代的平均大小是否大于老年代的剩余空間大小。如果大于,則進(jìn)行一次Full GC(老年代GC),如果小于,則查看HandlePromotionFailure設(shè)置是否允許擔(dān)保失敗,如果允許,那只會(huì)進(jìn)行一次Minor GC,如果不允許,則改為進(jìn)行一次Full GC。
5.目前會(huì)問到的****問題
1.年輕代三個(gè)區(qū)比例
Eden,S0,S1比例8:1:1
2.為什么要有Survivor區(qū)
為什么需要Survivor空間。我們看看如果沒有 Survivor 空間的話,垃圾收集將會(huì)怎樣進(jìn)行:一遍新生代 gc 過后,不管三七二十一,活著的對(duì)象全部進(jìn)入老年代,即便它在接下來的幾次 gc 過程中極有可能被回收掉。這樣的話老年代很快被填滿, Full GC 的頻率大大增加。我們知道,老年代一般都會(huì)被規(guī)劃成比新生代大很多,對(duì)它進(jìn)行垃圾收集會(huì)消耗比較長(zhǎng)的時(shí)間;如果收集的頻率又很快的話,那就更糟糕了?;谶@種考慮,虛擬機(jī)引進(jìn)了“幸存區(qū)”的概念:如果對(duì)象在某次新生代 gc 之后任然存活,讓它暫時(shí)進(jìn)入幸存區(qū);以后每熬過一次 gc ,讓對(duì)象的年齡+1,直到其年齡達(dá)到某個(gè)設(shè)定的值(比如15歲), JVM 認(rèn)為它很有可能是個(gè)“老不死的”對(duì)象,再呆在幸存區(qū)沒有必要(而且老是在兩個(gè)幸存區(qū)之間反復(fù)地復(fù)制也需要消耗資源),才會(huì)把它轉(zhuǎn)移到老年代。
Survivor的存在意義,就是減少被送到老年代的對(duì)象,進(jìn)而減少Full GC的發(fā)生,Survivor的預(yù)篩選保證,只有經(jīng)歷16次Minor GC還能在新生代中存活的對(duì)象,才會(huì)被送到老年代。
3.為什么有兩個(gè)Survivor區(qū)
為什么 Survivor 分區(qū)不能是 1 個(gè)?
如果 Survivor 分區(qū)是 1 個(gè)的話,假設(shè)我們把兩個(gè)區(qū)域分為 1:1,那么任何時(shí)候都有一半的內(nèi)存空間是閑置的,顯然空間利用率太低不是最佳的方案。
但如果設(shè)置內(nèi)存空間的比例是 8:2 ,只是看起來似乎“很好”,假設(shè)新生代的內(nèi)存為 100 MB( Survivor 大小為 20 MB ),現(xiàn)在有 70 MB 對(duì)象進(jìn)行垃圾回收之后,剩余活躍的對(duì)象為 15 MB 進(jìn)入 Survivor 區(qū),這個(gè)時(shí)候新生代可用的內(nèi)存空間只剩了 5 MB,這樣很快又要進(jìn)行垃圾回收操作,顯然這種垃圾回收器最大的問題就在于,需要頻繁進(jìn)行垃圾回收。
為什么 Survivor 分區(qū)是 2 個(gè)?
剛剛新建的對(duì)象在Eden中,經(jīng)歷一次Minor GC,Eden中的存活對(duì)象就會(huì)被移動(dòng)到第一塊survivor space S0,Eden被清空;等Eden區(qū)再滿了,就再觸發(fā)一次Minor GC,Eden和S0中的存活對(duì)象又會(huì)被復(fù)制送入第二塊survivor space S1(這個(gè)過程非常重要,因?yàn)檫@種復(fù)制算法保證了S1中來自S0和Eden兩部分的存活對(duì)象占用連續(xù)的內(nèi)存空間,避免了碎片化的發(fā)生)。S0和Eden被清空,然后下一輪S0與S1交換角色,如此循環(huán)往復(fù)。如果對(duì)象的復(fù)制次數(shù)達(dá)到16次,該對(duì)象就會(huì)被送到老年代中。下圖中每部分的意義和上一張圖一樣,就不加注釋了。
[圖片上傳失敗...(image-5d81f6-1589870920392)]
上述機(jī)制最大的好處就是,整個(gè)過程中,永遠(yuǎn)有一個(gè)survivor space是空的,另一個(gè)非空的survivor space無碎片。
那么,Survivor為什么不分更多塊呢?比方說分成三個(gè)、四個(gè)、五個(gè)?顯然,如果Survivor區(qū)再細(xì)分下去,每一塊的空間就會(huì)比較小,很容易導(dǎo)致Survivor區(qū)滿
總結(jié)
根據(jù)上面的分析可以得知,當(dāng)新生代的 Survivor 分區(qū)為 2 個(gè)的時(shí)候,不論是空間利用率還是程序運(yùn)行的效率都是最優(yōu)的,所以這也是為什么 Survivor 分區(qū)是 2 個(gè)的原因了。
6. JVM如何判定一個(gè)對(duì)象是否應(yīng)該被回收?(重點(diǎn)掌握)
判斷一個(gè)對(duì)象是否應(yīng)該被回收,主要是看其是否還有引用。判斷對(duì)象是否存在引用關(guān)系的方法包括引用計(jì)數(shù)法以及可達(dá)性分析。
引用計(jì)數(shù)法:
是一種比較古老的回收算法。原理是此對(duì)象有一個(gè)引用,即增加一個(gè)計(jì)數(shù),刪除一個(gè)引用則減少一個(gè)計(jì)數(shù)。垃圾回收時(shí),只需要收集計(jì)數(shù)為0的對(duì)象。此算法最致命的是無法處理循環(huán)引用的問題。
****可達(dá)性分析:
可達(dá)性分析的基本思路就是通過一系列可以做為root的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索。當(dāng)一個(gè)對(duì)象到root節(jié)點(diǎn)沒有任何引用鏈接時(shí),則證明此對(duì)象是可以被回收的。以下對(duì)象會(huì)被認(rèn)為是root對(duì)象:
- 棧內(nèi)存中引用的對(duì)象
- 方法區(qū)中靜態(tài)引用和常量引用指向的對(duì)象
- 被啟動(dòng)類(bootstrap加載器)加載的類和創(chuàng)建的對(duì)象
- Native方法中JNI引用的對(duì)象。
7. JVM垃圾回收算法有哪些?
HotSpot 虛擬機(jī)采用了可達(dá)性分析來進(jìn)行內(nèi)存回收,常見的回收算法有標(biāo)記-清除算法,復(fù)制算法和標(biāo)記整理算法。
標(biāo)記-清除算法(Mark-Sweep):
標(biāo)記-清除算法執(zhí)行分兩階段。
第一階段:從引用根節(jié)點(diǎn)開始標(biāo)記所有被引用的對(duì)象,
第二階段:遍歷整個(gè)堆,把未標(biāo)記的對(duì)象清除。此算法需要暫停整個(gè)應(yīng)用,并且會(huì)產(chǎn)生內(nèi)存碎片。

缺點(diǎn):
- 執(zhí)行效率不穩(wěn)定,會(huì)因?yàn)閷?duì)象數(shù)量增長(zhǎng),效率變低
- 標(biāo)記清除后會(huì)有大量的不連續(xù)的內(nèi)存碎片,空間碎片太多就會(huì)導(dǎo)致無法分配較大對(duì)象,無法找到足夠大的連續(xù)內(nèi)存,而發(fā)生gc
復(fù)制算法:
復(fù)制算法把內(nèi)存空間劃為兩個(gè)相等的區(qū)域,每次只使用其中一個(gè)區(qū)域。垃圾回收時(shí),遍歷當(dāng)前使用區(qū)域,把正在使用中的對(duì)象復(fù)制到另外一個(gè)區(qū)域中。復(fù)制算法每次只處理正在使用中的對(duì)象,因此復(fù)制成本比較小,同時(shí)復(fù)制過去以后還能進(jìn)行相應(yīng)的內(nèi)存整理,不會(huì)出現(xiàn)“碎片”問題。當(dāng)然,此算法的缺點(diǎn)也是很明顯的,就是需要兩倍內(nèi)存空間。

缺點(diǎn):
- 可用內(nèi)存縮成了一半,浪費(fèi)空間
標(biāo)記-整理算法:
標(biāo)記-整理算法結(jié)合了“標(biāo)記-清除”和“復(fù)制”兩個(gè)算法的優(yōu)點(diǎn)。也是分兩階段,
第一階段從根節(jié)點(diǎn)開始標(biāo)記所有被引用對(duì)象,
第二階段遍歷整個(gè)堆,清除未標(biāo)記對(duì)象并且把存活對(duì)象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“標(biāo)記-清除”的碎片問題,同時(shí)也避免了“復(fù)制”算法的空間問題。

8.垃圾收集器(掌握CMS和G1)
JVM中的垃圾收集器主要包括7種,即Serial,Serial Old,ParNew,Parallel Scavenge,Parallel Old以及CMS,G1收集器。如下圖所示:

1、Serial收集器:
Serial收集器是一個(gè)單線程的垃圾收集器,并且在執(zhí)行垃圾回收的時(shí)候需要 Stop The World。虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)新生代收集器。Serial收集器的優(yōu)點(diǎn)是簡(jiǎn)單高效,對(duì)于限定在單個(gè)CPU環(huán)境來說,Serial收集器沒有多線程交互的開銷。
2、Serial Old收集器:
Serial Old是Serial收集器的老年代版本,也是一個(gè)單線程收集器。主要也是給在Client模式下的虛擬機(jī)使用。在Server模式下存在主要是做為CMS垃圾收集器的后備預(yù)案,當(dāng)CMS并發(fā)收集發(fā)生Concurrent Mode Failure時(shí)使用。
3、ParNew收集器:
ParNew是Serial收集器的多線程版本,新生代是并行的(多線程的),老年代是串行的(單線程的),新生代采用復(fù)制算法,老年代采用標(biāo)記整理算法??梢允褂脜?shù):-XX:UseParNewGC使用該收集器,使用 -XX:ParallelGCThreads可以限制線程數(shù)量。
4、Parallel Scavenge垃圾收集器:
Parallel Scavenge是一種新生代收集器,使用復(fù)制算法的收集器,而且是并行的多線程收集器。Paralle收集器特點(diǎn)是更加關(guān)注吞吐量(吞吐量就是cpu用于運(yùn)行用戶代碼的時(shí)間與cpu總消耗時(shí)間的比值)??梢酝ㄟ^-XX:MaxGCPauseMillis參數(shù)控制最大垃圾收集停頓時(shí)間;通過-XX:GCTimeRatio參數(shù)直接設(shè)置吞吐量大??;通過-XX:+UseAdaptiveSizePolicy參數(shù)可以打開GC自適應(yīng)調(diào)節(jié)策略,該參數(shù)打開之后虛擬機(jī)會(huì)根據(jù)系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整虛擬機(jī)參數(shù)以提供最合適的停頓時(shí)間或者最大的吞吐量。自適應(yīng)調(diào)節(jié)策略是Parallel Scavenge收集器和ParNew的主要區(qū)別之一。
5、Parallel Old收集器:
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和標(biāo)記-整理算法。
6、CMS(Concurrent Mark Sweep)收集器(并發(fā)標(biāo)記清除)
CMS收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。CMS收集器是基于標(biāo)記-清除算法實(shí)現(xiàn)的,是一種老年代收集器,通常與ParNew一起使用。
CMS的垃圾收集過程分為4步:
- 初始標(biāo)記:需要“Stop the World”,初始標(biāo)記僅僅只是標(biāo)記一下GC Root能直接關(guān)聯(lián)到的對(duì)象,速度很快。
- 并發(fā)標(biāo)記:是主要標(biāo)記過程,這個(gè)標(biāo)記過程是和用戶線程并發(fā)執(zhí)行的。
- 重新標(biāo)記:需要“Stop the World”,為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄(停頓時(shí)間比初始標(biāo)記長(zhǎng),但比并發(fā)標(biāo)記短得多)。
- 并發(fā)清除:和用戶線程并發(fā)執(zhí)行的,基于標(biāo)記結(jié)果來清理對(duì)象。

那么問題來了,如果在重新標(biāo)記之前剛好發(fā)生了一次MinorGC,會(huì)不會(huì)導(dǎo)致重新標(biāo)記階段Stop the World時(shí)間太長(zhǎng)?
答:不會(huì)的,在并發(fā)標(biāo)記階段其實(shí)還包括了一次并發(fā)的預(yù)清理階段,虛擬機(jī)會(huì)主動(dòng)等待年輕代發(fā)生垃圾回收,這樣可以將重新標(biāo)記對(duì)象引用關(guān)系的步驟放在并發(fā)標(biāo)記階段,有效降低重新標(biāo)記階段Stop The World的時(shí)間。
CMS垃圾回收器的優(yōu)缺點(diǎn)分析:
CMS以降低垃圾回收的停頓時(shí)間為目的,很顯然其具有并發(fā)收集,停頓時(shí)間低的優(yōu)點(diǎn)。
缺點(diǎn)主要包括如下:
- 對(duì)CPU資源非常敏感,因?yàn)椴l(fā)標(biāo)記和并發(fā)清理階段和用戶線程一起運(yùn)行,當(dāng)CPU數(shù)變小時(shí),性能容易出現(xiàn)問題。
- 收集過程中會(huì)產(chǎn)生浮動(dòng)垃圾,所以不可以在老年代內(nèi)存不夠用了才進(jìn)行垃圾回收,必須提前進(jìn)行垃圾收集。通過參數(shù)-XX:CMSInitiatingOccupancyFraction的值來控制內(nèi)存使用百分比。如果該值設(shè)置的太高,那么在CMS運(yùn)行期間預(yù)留的內(nèi)存可能無法滿足程序所需,會(huì)出現(xiàn)Concurrent Mode Failure失敗,之后會(huì)臨時(shí)使用Serial Old收集器做為老年代收集器,會(huì)產(chǎn)生更長(zhǎng)時(shí)間的停頓。
- 標(biāo)記-清除方式會(huì)產(chǎn)生內(nèi)存碎片,可以使用參數(shù)-XX:UseCMSCompactAtFullCollection來控制是否開啟內(nèi)存整理(無法并發(fā),默認(rèn)是開啟的)。參數(shù)-XX:CMSFullGCsBeforeCompaction用于設(shè)置執(zhí)行多少次不壓縮的Full GC后進(jìn)行一次帶壓縮的內(nèi)存碎片整理(默認(rèn)值是0)。
接下來,我們先看下上邊介紹的浮動(dòng)垃圾是怎么產(chǎn)生的吧。
浮動(dòng)垃圾:
由于在應(yīng)用運(yùn)行的同時(shí)進(jìn)行垃圾回收,所以有些垃圾可能在垃圾回收進(jìn)行完成時(shí)產(chǎn)生,這樣就造成了“Floating Garbage”,這些垃圾需要在下次垃圾回收周期時(shí)才能回收掉。所以,并發(fā)收集器一般需要20%的預(yù)留空間用于這些浮動(dòng)垃圾。
7、G1(Garbage-First)收集器:
G1收集器將新生代和老年代取消了,取而代之的是將堆劃分為若干的區(qū)域,每個(gè)區(qū)域都可以根據(jù)需要扮演新生代的Eden和Survivor區(qū)或者老年代空間,仍然屬于分代收集器,區(qū)域的一部分包含新生代,新生代采用復(fù)制算法,老年代采用標(biāo)記-整理算法。
通過將JVM堆分為一個(gè)個(gè)的區(qū)域(region),G1收集器可以避免在Java堆中進(jìn)行全區(qū)域的垃圾收集。G1跟蹤各個(gè)region里面的垃圾堆積的價(jià)值大?。ɑ厥账@得的空間大小以及回收所需時(shí)間的經(jīng)驗(yàn)值),在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)回收時(shí)間來優(yōu)先回收價(jià)值最大的region。
G1收集器的特點(diǎn):
- 并行與并發(fā):G1能充分利用多CPU,多核環(huán)境下的硬件優(yōu)勢(shì),來縮短Stop the World,是并發(fā)的收集器。
- 分代收集:G1不需要其他收集器就能獨(dú)立管理整個(gè)GC堆,能夠采用不同的方式去處理新建對(duì)象、存活一段時(shí)間的對(duì)象和熬過多次GC的對(duì)象。
- 空間整合:G1從整體來看是基于標(biāo)記-整理算法,從局部(兩個(gè)Region)上看基于復(fù)制算法實(shí)現(xiàn),G1運(yùn)作期間不會(huì)產(chǎn)生內(nèi)存空間碎片。
- 可預(yù)測(cè)的停頓:能夠建立可以預(yù)測(cè)的停頓時(shí)間模型,預(yù)測(cè)停頓時(shí)間。
和CMS收集器類似,G1收集器的垃圾回收工作也分為了四個(gè)階段:
- 初始標(biāo)記
- 并發(fā)標(biāo)記
- 最終標(biāo)記
- 篩選回收
其中,篩選回收階段首先對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行計(jì)算,根據(jù)用戶期望的GC停頓時(shí)間來制定回收計(jì)劃。
9.Java常用版本垃圾收集器
1.首先說如果看怎么看
我的版本是jdk1.8
java -XX:+PrintCommandLineFlags -version

2.jdk1.8和1.9用的版本
jdk1.8默認(rèn)的新生代垃圾收集器:Parallel Scavenge,老年代:Parallel Old
jdk1.9 默認(rèn)垃圾收集器G1
