本文參考的是周志明的 《深刻理解Java虛擬機》第三章 ,爲了整理思路,簡單記錄一下,方便後期查閱。java
在垃圾收集器進行回收前,第一件事就是肯定這些對象哪些還存活,哪些已經死去。算法
在對象中添加一個引用計數器,每當有一個地方引用它時,計數器就加1;當引用失效時,計數器減1;其中計數器爲0的對象是不可能再被使用的已死對象。segmentfault
經過一系列的稱爲GC Roots
的對象做爲起始點,從這些節點開始向下搜索,搜索所通過
的路徑稱爲引用鏈(Reference Chain)
,當一個對象到GC Roots
沒有任何引用鏈相連(在圖論中稱爲對象不可達)時,這個對象就是不可用的。數組
圖片來源於網絡若有侵權請私信刪除
在java語言中,可做爲GC Roots
的對象包括:安全
java的引用能夠分爲強引用、軟引用、弱引用、虛引用:網絡
new
操做符建立的對象。只要強引用還存在,垃圾收集器就永遠不會回收掉被引用的對象。圖片來源於網絡若有侵權請私信刪除
要真正宣告一個對象死亡,至少要經歷兩次標記過程:數據結構
GC Roots
相鏈接的引用鏈,會被 第一次標記 而且進行一次篩選。篩選的條件是此對象是否有必要執行finalize()
方法(如當對象沒有重寫finalize()
方法或者finalize()
方法已經被虛擬機調用過則認爲沒有必要執行)。F-Queue
隊列中,並在稍後由一個由虛擬機本身創建的、低優先級的Finalizer
線程去執行它;稍後GC
將對F-Queue
中的對象進行第二次標記,若是對象仍是沒有被引用,則會被回收。可是做者不建議經過finalize()
方法「拯救」對象,由於它運行代價高、不肯定性大、沒法保證各個對象的調用順序。多線程
圖片來源於網絡若有侵權請私信刪除
不少人認爲方法區(HotSopt中的永久代)是沒有垃圾收集的,java虛擬機規範中也沒有要求須要對方法區實現垃圾收集。併發
永久代(方法區)的垃圾收集主要回收兩部份內容: 廢棄常量和無用的類
「abc」
已經進入了常量池中,可是當前系統沒有任何一個String對象是叫 作「abc」
的,換句話說,就是沒有任何String對象引用常量池中的「abc」
常量,也沒有其餘 - 地方引用了這個字面量,若是這時發生內存回收,並且必要的話,這個「abc」
常量就會被系 - 統清理出常量池。java.lang.Class
對象沒有被引用)。ClassLoader
已經被回收。java.lang.Class
對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法。算法分爲兩個階段:標記和清除jvm
標記:首先標記全部須要回收的對象
清除:在標記完成後統一回收全部被標記的對象
標記過程在上文宣告一個對象死亡過程當中說起
缺點
圖片來源於網絡若有侵權請私信刪除
思路:將可用內存按容量分爲兩個塊,每次只用其中之一。當這一塊內存用完以後,將還存活的對象複製到另外一邊去,而後清除全部已經使用過的部分。
優勢
缺點
解決方法
Eden空間
和兩塊較小的Survivor空間
,每次使用Eden
和其中一塊Survivor
。Eden
和兩塊Survivor
,默認比例爲8:1:1。代價是存在部份內存空間浪費,適合在新生代使用。圖片來源於網絡若有侵權請私信刪除
標記過程仍然與「標記-清除」算法同樣,但後續步驟不是直接對可回收對象進行清理,而是讓全部存活的對象都向一端移動,而後直接清理掉端邊界之外的內存。
圖片來源於網絡若有侵權請私信刪除
可達性分析的缺點
從GC Roots
節點找引用鏈這個操做爲例,可做爲GC Roots
的節點主要在全局性的引用(例如常量或類靜態屬性)與執行上下文(例如棧幀中的本地變量表)中,如今不少應用僅僅方法區就有數百兆,若是要逐個檢查這裏面的引用,那麼必然會消耗不少時間。
因爲要確保在一致性的快照中進行可達性分析,從而致使GC
進行時必需要停頓全部Java執行線程;
GC
,當執行系統停頓下來後並不須要一個不漏的檢查完全部執行上下文和全局的引用變量,虛擬機應當有辦法直接得知哪些地方存着對象的引用OopMap的數據結構**
來記錄哪些地方存着對象的引用JIT編譯
過程當中會在特定的位置記錄下棧和寄存器中哪些位置是引用判斷對象引用
OopMap
,只是在特定位置記錄了這些信息,這些位置稱爲安全點。GC
,只有到達安全點時才能暫停。搶先式中斷
GC
發生時,首先把全部線程所有中斷,若是發現有線程中斷的地方不在安全點上,就恢復線程,讓它「跑」到安全點上。 如今幾乎沒有虛擬機實現採用搶先式中斷來暫停線程從而響應GC
事件。主動式中斷
GC
須要中斷線程的時候,不直接對線程操做,僅僅簡單地設置一個標誌,各個線程執行時主動去輪詢這個標誌,發現中斷標誌爲真時就本身中斷掛起。輪詢標誌的地方和安全點是重合的,另外再加上建立對象須要分配內存的地方。二者的區別在於,搶先式中斷是不管如何都進行中斷,而主動式中斷則是線程執行輪詢標誌查看是否中斷。
GC
都是安全的。GC
收集器。JVM
在GC
時就無論這些線程了。圖片來源於網絡若有侵權請私信刪除
Serial
收集器的多線程版本Server
模式下虛擬機中首選的新生代收集器圖片來源於網絡若有侵權請私信刪除
與其餘收集器關注於盡量縮短垃圾收集時用戶線程停頓時間不一樣,它的目標是達到一個可控制的吞吐量。
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量
-XX:MaxGCPauseMillis
參數。-XX:GCTimeRatio
參數。Serial Old是Serial收集器的老年代版本,它一樣是一個單線程收集器,使用「標記-整理」
算法。
圖片來源於網絡若有侵權請私信刪除
Serial Old收集器是Serail收集器的老年代版本,是一個單線程收集器,使用標記-整理算法。
圖片來源於網絡若有侵權請私信刪除
Parallel Old是Parallel Scavenge
收集器的老年代版本,使用多線程和「標記-整理」
算法。
圖片來源於網絡若有侵權請私信刪除
CMS收集器是一種以獲取最短的回收停頓時間爲目標的收集器。
CMS收集器基於標記-清楚算法實現,分爲四個步驟:初始標記、併發標記、從新標記、併發清除。
步驟詳解
GC Roots
能直接關聯到的對象,速度很快。GC Roots Tracing
。(CMS concurrent sweep)
。圖片來源於網絡若有侵權請私信刪除
G1收集器是一款面向服務端應用的垃圾收集器。
G1收集器具有如下特色:
並行與併發
Stop-The-World
停頓的時間,部分其餘收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓Java程序繼續執行。分代收集
G1
中依然得以保留。 雖然G1
能夠不須要其餘收集器配合就能獨立管理整個GC
堆,但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、 熬過屢次GC
的舊對象以獲取更好的收集效果。空間整合
可預測的停頓
G1
相對於CMS
的另外一大優點,下降停頓時間是G1
和CMS
共同的關注點,但G1
除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M
毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已是實時Java(RTSJ)
的垃圾收集器的特徵了。G1
收集器將整個Java堆劃分爲多個大小相等的獨立區域,雖然還保留有新生代和老生代的概念,但新生代和老生代再也不是物理隔的了,他們是一部分Region
的集合。
G1收集器能夠有計劃地避免在整個Java堆中進行全區域的垃圾收集:跟蹤各個Region
裏面的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region
。
在G1
收集器中,使用Remembered Set
來避免全堆掃描
G1
收集器的運做大體可劃分爲如下幾個步驟:
初始標記(Initial Marking)
GC Roots
能直接關聯到的對象,而且修改TAMS(Next Top at Mark Start)
的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這階段須要停頓線程,但耗時很短。併發標記(Concurrent Marking)
GC Root
開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。最終標記(Final Marking)
Remembered Set Logs
裏面,最終標記階段須要把Remembered Set Logs
的數據合併到Remembered Set
中,這階段須要停頓線程,可是可並行執行。篩選回收(Live Data Counting and Evacuation)
Region
的回收價值和成本進行排序,根據用戶所指望的GC
停頓時間來制定回收計劃圖片來源於網絡若有侵權請私信刪除
圖片來源於網絡若有侵權請私信刪除
GC
發生的時間(虛擬機啓動之後的秒殺)「[GC」
和「[Full GC」
說明停頓類型,有Full
表明的是Stop-The-World
的;「[DefNew」
、「[Tenured」
和「[Perm」
表示GC發生的區域;「3324K -> 152K(3712K)」
含義是 「GC
前該內存已使用容量 -> GC
後該內存區域已使用容量(該區域總容量)」;「3324K -> 152K(11904)」
含義是 「GC
前Java堆已使用容量 -> GC
後Java堆已使用容量(Java堆總容量)」;「0.0025925 secs」
表示該內存區域GC
所佔用的時間;垃圾收集器參數總結
-XX:+<option>
啓用選項-XX:-<option>
不啓用選項-XX:<option>=<number>
-XX:<option>=<string>
參數 | 描述 |
---|---|
UserSerialGC | 虛擬機在client模式下的默認值,打開此開關後,用於Serial+Serial Old的收集器組合進行內存回收 |
UserParNewGC | 打開此開關 使用ParNew + Serial Old收集器組合進行內存回收 |
UseConcMarkSweepGC | 打開此開關,使用ParNew+CMS+Serial Old收集器組合進行內存回收。Serial Old在CMS收集器出現concurrent Mode Failure 失敗後的後備收集器 |
UseParallelGC | 在server模式下的默認值,打開此開關後使用Scavenge+Serial Old收集器組合進行回收 |
UseParallelOldGC | 打開此開關後使用 Parallel Scavenge+Parallel Old收集器組合進行內存回收 |
SurvivorRatio | 新生代中Eden區域與Survivor區域的比值,默認爲8,表示Eden:Survivor=8:1 |
PretenureSizeThreshold | 直接晉升到老年代對象的大小,設置這個參數後大於這個參數的對象直接在老年代中分配 |
MaxTenuringThreshold | 晉升老年代對象的年齡,每一個對象堅持一次MnorGC年齡就加一,當超過這個參數值就進入老年代 |
UseAdaptiveSizePolicy | 動態調整java堆各個區域的大小以及進入老年代的年齡 |
HandlePromotionFailure | 是否容許分配擔保失敗,即老年代剩餘空間不足以應付新生代整個對象都存活的特殊狀況 |
ParalleGCThreads | 設置並行GC時進行內存回收的線程數 |
GCTimeratio | GC時間佔總時間比率,默認值爲99,容許1%的GC時間。只在Parallel Seavenge收集器時生效 |
MaxGCPauseMillis | 設置GC的最大停頓時間,只在Parallel Seavenge收集器時生效 |
CMSInitiatingOccupancyFration | 設置CMS老年代空間被使用多少後觸發GC,默認值爲68%,只在CMS收集器時生效 |
UseCMSCompactAtFullCollection | 設置CMS收集器完成垃圾收集後是否須要進行一次碎片整理,只在CMS垃圾收集器時生效 |
CMSFullGCBeforeCompaction | 設置CMS收集器進行若干次垃圾收集後再啓動一次內存碎片整理,只在CMS垃圾收集器時生效 |
對象優先在新生代分配
大對象直接進入老年代
長期存活的對象將進入老年代
大多數狀況下,對象優先在新生代的Eden區分配。
當Eden區沒有足夠的空間時,虛擬機將發起一次Minor GC。
Minor GC與Full GC。
PretenureSizeThreshold
,大於這個參數的對象將直接在老年代分配。MaxTenuringThreshold
來設置。4.動態對象年齡的斷定
爲何程序要跑到安全點時停下來?
Oop(Ordinary Object Pointer)
會須要大量的額外空間,增大GC的空間成本。設置了合適的安全點,有助於虛擬機得知對象引用所在的地方,所以有利於GC對「即將回收」的對象進行掃描。最後上一張本章結構圖
圖片來源於網絡若有侵權請私信刪除
轉至:https://segmentfault.com/a/1190000010421285