一。引用計數算法java
比較古老的回收算法。原理是此對象有一個引用,即增長一個計數,刪除一個引用則減小一個計數。算法
垃圾回收時,只用收集計數爲0的對象。此算法最致命的是沒法處理循環引用的問題。數組
兩種實現方式緩存
侵入式與非侵入性,引用計數算法的垃圾收集通常有侵入式與非侵入式兩種,侵入式的實現就是將引用計數器直接根植在對象內部,用C++的思想進行解釋就是,在對象的構造或者拷貝構造中進行加一操做,在對象的析構中進行減一操做,非侵入式恩想就是有一塊單獨的內存區域,用做引用計數器多線程
圖解說明以下:併發
二。根搜索算法佈局
定義:根搜索方法是經過一些「GC Roots」對象做爲起點,從這些節點開始往下搜索,搜索經過的路徑成爲引用鏈(Reference Chain),當一個對象沒有被GC Roots的引用鏈鏈接的時候,說明這個對象是不可用的,這樣的對象被斷定爲是可回收的。優化
java中能夠做爲GC Roots對象包括如下幾種:spa
1.虛擬機棧(棧幀中的本地變量表)中的引用對象。線程
2.方法區中的類靜態屬性引用的對象。
3.方法區中的常量引用的對象。
4.本地方法棧中JNI(也即通常說的Native方法)的引用的對象。
根搜索算法判斷對象是否存活與引用有關。java將引用分爲四類:強引用、軟引用、弱引用、虛引用,這四種引用強度依次逐步減弱。
強引用:就是咱們通常聲明對象是時虛擬機生成的引用,強引用環境下,垃圾回收時須要嚴格判斷當前對象是否被強引用,若是被強引用,則不會被垃圾回收。
軟引用:軟引用通常被作爲緩存來使用。與強引用的區別是,軟引用在垃圾回收時,虛擬機會根據當前系統的剩餘內存來決定是否對軟引用進行回收。若是剩餘內存比較緊張,則虛擬機會回收軟引用所引用的空間;若是剩餘內存相對富裕,則不會進行回收。換句話說,虛擬機在發生OutOfMemory時,確定是沒有軟引用存在的。
弱引用:弱引用與軟引用相似,都是做爲緩存來使用。但與軟引用不一樣,弱引用在進行垃圾回收時,是必定會被回收掉的,所以其生命週期只存在於一個垃圾回收週期內。
虛引用:與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。它不能單獨使用,必須和引用隊列聯合使用。虛引用的主要做用是跟蹤對象被垃圾回收的狀態。
圖解說明以下所示:
具體解析:
根搜索算法中不可達的對象並不是「非死不可」,這時候它們暫時處於「緩刑」階段,真正宣告一個對象死亡,至少要經歷兩次標記過程。若是經過根搜索後發現沒有與GC Roots相連的引用鏈相連。它將會被第一次標記而且會進行篩選,當對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,虛擬機將這兩種狀況都視爲沒有必要執行finalize()方法。
若是這個對象被斷定爲有必要執行finalize()方法,那麼這個對象將會被放置在一個名爲F-Queue的隊列之中,並在稍後有一條虛擬機自動創建、低優先級的 finalize的線程去執行。虛擬機會觸發finalize()方法,但並不承諾等待它執行結束。finalize()方法是對象逃脫死亡的最後一次F-Queue中的對象機會。GC將會對F-QUEUEF-Queue中的對象進行第二次小規模的標記,若是某個對象從新與GC Roots引用鏈上的對象創建關聯關係,那麼第二次標記時它將被移除F-Queue。
任何一個對象的finalize()方法都只會被系統自動調用一次,若是對象面臨下一次回收,它的finalize()方法不會被再次執行。
三。標記-清除算法
定義:標記清除算法是最基礎的收集算法,其餘收集算法都是基於這種思想。標記清除算法分爲「標記」和「清除」兩個階段:首先標記出須要回收的對象,標記完成以後統一清除對象。
算法過程:
標記-清除算法是現代垃圾回收算法的思想基礎。標記-清除算法將垃圾回收分爲兩個階段:
標記階段和清除階段。一種可行的實現是,在標記階段,首先經過根節點,標記全部從根節點開始的可達對象。
所以,未被標記的對象就是未被引用的垃圾對象;而後,在清除階段,清除全部未被標記的對象。
圖解說明一下:
標記-清除算法詳解:
它的作法是當堆中的有效內存空間(available memory)被耗盡的時候,就會中止整個程序(也被成爲stop the world),而後進行兩項工做,第一項則是標記,第二項則是清除。
也就是說,就是當程序運行期間,若可使用的內存被耗盡的時候,GC線程就會被觸發並將程序暫停,隨後將依舊存活的對象標記一遍,最終再將堆中全部沒被標記的對象所有清除掉,接下來便讓程序恢復運行。
標記-清除算法的缺點:
(1)效率低:效率比較低(遞歸與全堆對象遍歷),致使stop the world的時間比較長,尤爲對於交互式的應用程序來講簡直是沒法接受。
(2)空間問題:這種方式清理出來的空閒內存是不連續的,這點不難理解,咱們的死亡對象都是隨即的出如今內存的各個角落的,如今把它們清除以後,內存的佈局天然會亂七八糟。而爲了應付這一點,JVM就不得不維持一個內存的空閒列表,這又是一種開銷。並且在分配數組對象的時候,尋找連續的內存空間會不太好找。
四。複製算法
將內存平均分紅A、B兩塊,算法過程:
上圖中能夠看到,標記的存活對象將會被整理,按照內存地址依次排列,而未被標記的內存會被清理掉。如此一來,當咱們須要給新對象分配內存時,JVM只須要持有一個內存的起始地址便可,這比維護一個空閒列表顯然少了許多開銷。
標記/整理算法不只能夠彌補標記/清除算法當中,內存區域分散的缺點,也消除了複製算法當中,內存減半的高額代價。
不只要標記全部存活對象,還要整理全部存活對象的引用地址。從效率上來講,標記/整理算法要低於複製算法。(具體效果,須要根據實際狀況而定)
六。分代收集算法
當前商業虛擬機的GC都是採用的「分代收集算法」,這並非什麼新的思想,只是根據對象的存活週期的不一樣將內存劃分爲幾塊兒。通常是把Java堆分爲新生代和老年代:短命對象歸爲新生代,長命對象歸爲老年代。
注:老年代的對象中,有一小部分是由於在新生代回收時,老年代作擔保,進來的對象;絕大部分對象是由於不少次GC都沒有被回收掉而進入老年代。
七。串行收集(拓展)
串行收集使用單線程處理全部垃圾回收工做,由於無需多線程交互,實現容易,並且效率比較高。可是,其侷限性也比較明顯,即沒法使用多處理器的優點,因此此收集適合單處理器機器。固然,此收集器也能夠用在小數據量(100M左右)狀況下的多處理器機器上。
八。並行收集(拓展)
並行收集使用多線程處理垃圾回收工做,於是速度快,效率高。並且理論上CPU數目越多,越能體現出並行收集器的優點。
九。併發收集(拓展)
相對於串行收集和並行收集而言,前面兩個在進行垃圾回收工做時,須要暫停整個運行環境,而只有垃圾回收程序在運行,所以,系統在垃圾回收時會有明顯的暫停,並且暫停時間會由於堆越大而越長。