1.什么是GC?
大白話說就是垃圾回識訓制,記憶體空間是有限的,你創建的每個物件和變數都會占據記憶體,gc做的就是物件清除將記憶體釋放出來,這就是GC要做的事,
2.需要GC的區域
說起垃圾回收的場所,了解過JVM(Java Virtual Machine Model)記憶體模型的朋友應該會很清楚,堆是Java虛擬機進行垃圾回收的主要場所,其次要場所是方法區,
3.堆記憶體的結構(1.7)
在JDK1.8之后,堆的永久區取消了
由元空間取代
Java將堆記憶體分為3大部分:新生代、老年代和永久代,其中新生代又進一步劃分為Eden、S0、S1(Survivor)三個區

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

缺點:
- 執行效率不穩定,會因為物件數量增長,效率變低
- 標記清除后會有大量的不連續的記憶體碎片,空間碎片太多就會導致無法分配較大物件,無法找到足夠大的連續記憶體,而發生gc
復制演算法:
復制演算法把記憶體空間劃為兩個相等的區域,每次只使用其中一個區域,垃圾回收時,遍歷當前使用區域,把正在使用中的物件復制到另外一個區域中,復制演算法每次只處理正在使用中的物件,因此復制成本比較小,同時復制過去以后還能進行相應的記憶體整理,不會出現“碎片”問題,當然,此演算法的缺點也是很明顯的,就是需要兩倍記憶體空間,

缺點:
- 可用記憶體縮成了一半,浪費空間
標記-整理演算法:
標記-整理演算法結合了“標記-清除”和“復制”兩個演算法的優點,也是分兩階段,
第一階段從根節點開始標記所有被參考物件,
第二階段遍歷整個堆,清除未標記物件并且把存活物件“壓縮”到堆的其中一塊,按順序排放,此演算法避免了“標記-清除”的碎片問題,同時也避免了“復制”演算法的空間問題,

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

1、Serial收集器:
Serial收集器是一個單執行緒的垃圾收集器,并且在執行垃圾回收的時候需要 Stop The World,虛擬機運行在Client模式下的默認新生代收集器,Serial收集器的優點是簡單高效,對于限定在單個CPU環境來說,Serial收集器沒有多執行緒互動的開銷,
2、Serial Old收集器:
Serial Old是Serial收集器的老年代版本,也是一個單執行緒收集器,主要也是給在Client模式下的虛擬機使用,在Server模式下存在主要是做為CMS垃圾收集器的后備預案,當CMS并發收集發生Concurrent Mode Failure時使用,
3、ParNew收集器:
ParNew是Serial收集器的多執行緒版本,新生代是并行的(多執行緒的),老年代是串行的(單執行緒的),新生代采用復制演算法,老年代采用標記整理演算法,可以使用引數:-XX:UseParNewGC使用該收集器,使用 -XX:ParallelGCThreads可以限制執行緒數量,
4、Parallel Scavenge垃圾收集器:
Parallel Scavenge是一種新生代收集器,使用復制演算法的收集器,而且是并行的多執行緒收集器,Paralle收集器特點是更加關注吞吐量(吞吐量就是cpu用于運行用戶代碼的時間與cpu總消耗時間的比值),可以通過-XX:MaxGCPauseMillis引數控制最大垃圾收集停頓時間;通過-XX:GCTimeRatio引數直接設定吞吐量大小;通過-XX:+UseAdaptiveSizePolicy引數可以打開GC自適應調節策略,該引數打開之后虛擬機會根據系統的運行情況收集性能監控資訊,動態調整虛擬機引數以提供最合適的停頓時間或者最大的吞吐量,自適應調節策略是Parallel Scavenge收集器和ParNew的主要區別之一,
5、Parallel Old收集器:
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多執行緒和標記-整理演算法,
6、CMS(Concurrent Mark Sweep)收集器(并發標記清除)
CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,CMS收集器是基于標記-清除演算法實作的,是一種老年代收集器,通常與ParNew一起使用,
CMS的垃圾收集程序分為4步:
- 初始標記:需要“Stop the World”,初始標記僅僅只是標記一下GC Root能直接關聯到的物件,速度很快,
- 并發標記:是主要標記程序,這個標記程序是和用戶執行緒并發執行的,
- 重新標記:需要“Stop the World”,為了修正并發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分物件的標記記錄(停頓時間比初始標記長,但比并發標記短得多),
- 并發清除:和用戶執行緒并發執行的,基于標記結果來清理物件,

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

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/21248.html
標籤:其他
上一篇:html+css面試合集
下一篇:Java類加載機制
