大白話說就是垃圾回收機制,內存空間是有限的,你建立的每一個對象和變量都會佔據內存,gc作的就是對象清除將內存釋放出來,這就是GC要作的事。java
提及垃圾回收的場所,瞭解過JVM(Java Virtual Machine Model)內存模型的朋友應該會很清楚,堆是Java虛擬機進行垃圾回收的主要場所,其次要場所是方法區。算法
Java將堆內存分爲3大部分:新生代、老年代和永久代,其中新生代又進一步劃分爲Eden、S0、S1(Survivor)三個區bootstrap
咱們建立的對象會優先在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很是頻繁,並且速度也很快。spa
Full GC(老年代GC):
Full GC是指發生在老年代的GC,當老年代沒有足夠的空間時即發生Full GC,發生Full GC通常都會有一次Minor GC。線程
接下來,咱們來看關於內存分配與回收的兩個重要概念吧。3d
動態對象年齡斷定:
若是Survivor空間中相同年齡全部對象的大小總和大於Survivor空間的一半,那麼年齡大於等於該對象年齡的對象便可晉升到老年代,沒必要要等到-XX:MaxTenuringThreshold。
空間分配擔保:
發生Minor GC時,虛擬機會檢測以前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小。若是大於,則進行一次Full GC(老年代GC),若是小於,則查看HandlePromotionFailure設置是否容許擔保失敗,若是容許,那隻會進行一次Minor GC,若是不容許,則改成進行一次Full GC。
Eden,S0,S1比例8:1:1
這個我用別人說的話解釋一下:
連接:https://www.jianshu.com/p/2caad185ee1f
爲何須要Survivor空間。咱們看看若是沒有 Survivor 空間的話,垃圾收集將會怎樣進行:一遍新生代 gc 事後,無論三七二十一,活着的對象所有進入老年代,即使它在接下來的幾回 gc 過程當中極有可能被回收掉。這樣的話老年代很快被填滿, Full GC 的頻率大大增長。咱們知道,老年代通常都會被規劃成比新生代大不少,對它進行垃圾收集會消耗比較長的時間;若是收集的頻率又很快的話,那就更糟糕了。基於這種考慮,虛擬機引進了「倖存區」的概念:若是對象在某次新生代 gc 以後任然存活,讓它暫時進入倖存區;之後每熬過一次 gc ,讓對象的年齡+1,直到其年齡達到某個設定的值(好比15歲), JVM 認爲它頗有多是個「老不死的」對象,再呆在倖存區沒有必要(並且總是在兩個倖存區之間反覆地複製也須要消耗資源),纔會把它轉移到老年代。
Survivor的存在乎義,就是減小被送到老年代的對象,進而減小Full GC的發生,Survivor的預篩選保證,只有經歷16次Minor GC還能在新生代中存活的對象,纔會被送到老年代。
爲何 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 個的緣由了。
判斷一個對象是否應該被回收,主要是看其是否還有引用。判斷對象是否存在引用關係的方法包括引用計數法以及可達性分析。
是一種比較古老的回收算法。原理是此對象有一個引用,即增長一個計數,刪除一個引用則減小一個計數。垃圾回收時,只須要收集計數爲0的對象。此算法最致命的是沒法處理循環引用的問題。
可達性分析的基本思路就是經過一系列能夠作爲root的對象做爲起始點,從這些節點開始向下搜索。當一個對象到root節點沒有任何引用連接時,則證實此對象是能夠被回收的。如下對象會被認爲是root對象:
HotSpot 虛擬機採用了可達性分析來進行內存回收,常見的回收算法有標記-清除算法,複製算法和標記整理算法。
標記-清除算法(Mark-Sweep):
標記-清除算法執行分兩階段。
第一階段:從引用根節點開始標記全部被引用的對象,
第二階段:遍歷整個堆,把未標記的對象清除。此算法須要暫停整個應用,而且會產生內存碎片。
缺點:
複製算法:
複製算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中。複製算法每次只處理正在使用中的對象,所以複製成本比較小,同時複製過去之後還能進行相應的內存整理,不會出現「碎片」問題。固然,此算法的缺點也是很明顯的,就是須要兩倍內存空間。
缺點:
標記-整理算法:
標記-整理算法結合了「標記-清除」和「複製」兩個算法的優勢。也是分兩階段,
第一階段從根節點開始標記全部被引用對象,
第二階段遍歷整個堆,清除未標記對象而且把存活對象「壓縮」到堆的其中一塊,按順序排放。此算法避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題。
JVM中的垃圾收集器主要包括7種,即Serial,Serial Old,ParNew,Parallel Scavenge,Parallel Old以及CMS,G1收集器。以下圖所示:
一、Serial收集器:
Serial收集器是一個單線程的垃圾收集器,而且在執行垃圾回收的時候須要 Stop The World。虛擬機運行在Client模式下的默認新生代收集器。Serial收集器的優勢是簡單高效,對於限定在單個CPU環境來講,Serial收集器沒有多線程交互的開銷。
二、Serial Old收集器:
Serial Old是Serial收集器的老年代版本,也是一個單線程收集器。主要也是給在Client模式下的虛擬機使用。在Server模式下存在主要是作爲CMS垃圾收集器的後備預案,當CMS併發收集發生Concurrent Mode Failure時使用。
三、ParNew收集器:
ParNew是Serial收集器的多線程版本,新生代是並行的(多線程的),老年代是串行的(單線程的),新生代採用複製算法,老年代採用標記整理算法。可使用參數:-XX:UseParNewGC使用該收集器,使用 -XX:ParallelGCThreads能夠限制線程數量。
四、Parallel Scavenge垃圾收集器:
Parallel Scavenge是一種新生代收集器,使用複製算法的收集器,並且是並行的多線程收集器。Paralle收集器特色是更加關注吞吐量(吞吐量就是cpu用於運行用戶代碼的時間與cpu總消耗時間的比值)。能夠經過-XX:MaxGCPauseMillis參數控制最大垃圾收集停頓時間;經過-XX:GCTimeRatio參數直接設置吞吐量大小;經過-XX:+UseAdaptiveSizePolicy參數能夠打開GC自適應調節策略,該參數打開以後虛擬機會根據系統的運行狀況收集性能監控信息,動態調整虛擬機參數以提供最合適的停頓時間或者最大的吞吐量。自適應調節策略是Parallel Scavenge收集器和ParNew的主要區別之一。
五、Parallel Old收集器:
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和標記-整理算法。
CMS收集器是一種以獲取最短回收停頓時間爲目標的收集器。CMS收集器是基於標記-清除算法實現的,是一種老年代收集器,一般與ParNew一塊兒使用。
CMS的垃圾收集過程分爲4步:
那麼問題來了,若是在從新標記以前恰好發生了一次MinorGC,會不會致使從新標記階段Stop the World時間太長?
答:不會的,在併發標記階段其實還包括了一次併發的預清理階段,虛擬機會主動等待年輕代發生垃圾回收,這樣能夠將從新標記對象引用關係的步驟放在併發標記階段,有效下降從新標記階段Stop The World的時間。
CMS以下降垃圾回收的停頓時間爲目的,很顯然其具備併發收集,停頓時間低的優勢。
接下來,咱們先看下上邊介紹的浮動垃圾是怎麼產生的吧。
浮動垃圾:
因爲在應用運行的同時進行垃圾回收,因此有些垃圾可能在垃圾回收進行完成時產生,這樣就形成了「Floating Garbage」,這些垃圾須要在下次垃圾回收週期時才能回收掉。因此,併發收集器通常須要20%的預留空間用於這些浮動垃圾。
七、G1(Garbage-First)收集器:
G1收集器將新生代和老年代取消了,取而代之的是將堆劃分爲若干的區域,每一個區域均可以根據須要扮演新生代的Eden和Survivor區或者老年代空間,仍然屬於分代收集器,區域的一部分包含新生代,新生代採用複製算法,老年代採用標記-整理算法。
經過將JVM堆分爲一個個的區域(region),G1收集器能夠避免在Java堆中進行全區域的垃圾收集。G1跟蹤各個region裏面的垃圾堆積的價值大小(回收所得到的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據回收時間來優先回收價值最大的region。
G1收集器的特色:
和CMS收集器相似,G1收集器的垃圾回收工做也分爲了四個階段:
其中,篩選回收階段首先對各個Region的回收價值和成本進行計算,根據用戶指望的GC停頓時間來制定回收計劃。
1.首先說若是看怎麼看
個人版本是jdk1.8
java -XX:+PrintCommandLineFlags -version
2.jdk1.8和1.9用的版本
jdk1.8默認的新生代垃圾收集器:Parallel Scavenge,老年代:Parallel Old
jdk1.9 默認垃圾收集器G1