java 垃圾回收步步深入03----垃圾收集算法

                               java 垃圾回收步步深入03----垃圾收集算法

1.常用垃圾回收機制

1)標記-清除收集器

  這種收集器首先遍歷對象圖並標記可到達的對象,然後掃描堆棧以尋找未標記對象並釋放它們的內存。這種收集器一般使用單線程工作並停止其他操作。

2)標記-壓縮收集器

  有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,則把標記對象複製到堆棧的新域中以便壓縮堆棧。這種收集器也停止其他操作。

3)複製收集器

這 種收集器將堆棧分爲兩個域,常稱爲半空間。每次僅使用一半的空間,虛擬機生成的新對象則放在另一半空間中。垃圾回收器運行時,它把可到達對象複製到另一半 空間,沒有被複制的的對象都是不可達對象,可以被回收。這種方法適用於短生存期的對象,持續複製長生存期的對象由於多次拷貝,導致效率降低。缺點是隻有一 半的虛擬機空間得到使用。

4)增量收集器

增量收集器把堆棧分爲多個域,每次僅從一個域收集垃圾。這會造成較小的應用程序中斷。

5)分代收集器

這種收集器把堆棧分爲兩個或多個域,用以存放不同壽命的對象。虛擬機生成的新對象一般放在其中的某個域中。過一段時間,繼續存在的對象將獲得使用期並轉入更長壽命的域中。分代收集器對不同的域使用不同的算法以優化性能。這樣可以減少複製對象的時間。

6)併發收集器

併發收集器與應用程序同時運行。這些收集器在某點上(比如壓縮時)一般都不得不停止其他操作以完成特定的任務,但是因爲其他應用程序可進行其他的後臺操作,所以中斷其他處理的實際時間大大降低。

7)並行收集器

並行收集器使用某種傳統的算法並使用多線程並行的執行它們的工作。在多CPU機器上使用多線程技術可以顯著的提高java應用程序的可擴展性。

 8)自適應收集器

根據程序運行狀況以及堆的使用狀況,自動選一種合適的垃圾回收算法。這樣可以不侷限與一種垃圾回收算法。

2.垃圾回收算法

在Java中可以作爲根的對象包括以下幾種:

  1. 虛擬機棧中引用的對象;
  2. 方法區中的類靜態屬性引用的對象;
  3. 方法區中常量引用的對象;
  4. 本地方法棧中JNI的引用的對象;

那麼,如何回收?

  其實對象並非只有“有用”和“沒用”這兩種狀態,因爲在GC之前還會有機會調用finalize方法,在改方法中對象可能復活(但是隻能復活一次)。那麼,指向對象的引用可能有以下四種:

  1. 強引用;
  2. 軟引用;
  3. 弱引用;
  4. 虛引用;

a."標記-清除算法"

最簡單的回收算法是“標記-清除算法”,其實就是標記出來垃圾然後將其釋放:

從上圖中也非常容易看到這種算法的缺點:容易產生內存碎片,另一個不太明顯的缺點就是效率低。爲什麼效率低呢?因爲堆上面大部分的對象是垃圾(至於爲什麼就不多說了),而在該算法中刪除的過程重要處理的就是這部分,所以會慢一點。

b.“複製算法” 

複製算法”正好可以解決這個問題:

實現的時候將內存分成兩部分,每次只是用其中的一部分,在進行垃圾回收的時候將正在使用的對象拷貝到另一半的內存中。但是,這樣很明顯會浪費很多的內存,那麼怎麼優化呢?還是從垃圾比較多的角度來想。每次回收之後的對象佔用的空間其實很小的,也就是隻要很少的內存,也就是:

較大的一塊內存區域是Eden,還有兩個較小的Survivor,具體的過程如上(兩個Surivior交替使用)。當然Survivor的大小可能不夠用,這個時候就需要“內存擔保”了。那麼現在再來看下垃圾回收的過程中還有沒有做一些無用功?堆上的一些對象可能有很長的生命週期(在Web應用中更是如此),那麼這些對象就會被不停地在兩個Survivor之間拷來拷去。這樣也就有了“分代收集算法”:

c.分代收集算法

  其實分代收集並沒有什麼特別的地方,如果一個對象的生命週期比較長,那麼就把他放到老年代中。那怎麼去預測一個對象的生命長短?一個很簡單的方法就是如果活了足夠久的時間,那麼我們就認爲這個對象會繼續活很久(當然這個閥值可以去動態計算),當然也有可能剛進入老年代就變成垃圾了,不過這個概率應該很小。老年代是沒有內存擔保的(還有一個原因就是老年代中的存活率很高),所以適合用“標記-清理”的方式來收集。

上面的這些算法都有一個共同的問題:垃圾回收都是全量的。如果全量收集消耗了很多的時間,那麼用戶程序的體驗就會下降很多(尤其是搜索引擎)。那麼每次不是全量地,而是隻回收一部分的垃圾,這樣就能減少一次回收的時間(也就減少了用戶程序的間隔),那麼就有了“火車算法”:

 

首先將內存分成不同的塊,回收也是針對塊來操作的,所以檢查的是有沒有外部的引用指向塊內的對象,主要的操作如下:

  1. 新的對象要不被打包成車廂掛到現有的火車尾部,或者當成一輛新的火車進站;
  2. 任何被其他車廂引用的對象都移出去;
    1. 如果存在來自非成熟對象空間的引用,將其移到其他的火車上去;
    2. 如果被其他火車引用,移動到對應的火車上去;
    3. 1、2完成之後,將被本列火車引用的對象移動到最後一節車廂去;
  3. 當然,可能編號最小的車廂整個就被回收了;

 3. 火車算法

            垃圾收集算法一個很大的缺點就是難以控制垃圾回收所佔用的CPU時間,以及何時需要進行垃圾回收。火車算法是分代收集器所用的算法,目的是在成熟對象空間中提供限定時間的漸進收集。目前應用於SUN公司的Hotspot虛擬機上。

              在火車算法中,內存被分爲塊,多個塊組成一個集合。爲了形象化,一節車廂代表一個塊,一列火車代表一個集合,見圖一

圖一

注意每個車廂大小相等,但每個火車包含的車廂數不一定相等。垃圾收集以車廂爲單位,收集順序依次爲1.11.21.31.42.12.22.33.13.23.3。這個順序也是塊被創建的先後順序。

垃圾收集器先從塊1.1開始掃描直到1.4,如果火車1四個塊中的所有對象沒有被火車2和火車3的對象引用,而只有火車1內部的對象相互引用,則整個火車1都是垃圾,可以被回收。

圖二,車廂1.1中有對象A和對象B1.3中有對象C1.4中有對象D車廂2.2中有對象E,車廂3.3中有對象F。在火車1中,對象C引用對象A,對象B引用對象D,可見,火車2和火車3沒有引用火車1的對象,則整個火車1都是垃圾。

圖二

如果火車1中有對象被其它火車引用,見圖三,掃描車廂1.1時發現對象A被火車2中的E引用,則將對象A從車廂1.1轉移到車廂2.2,然後掃描A引用的對象D,把D也轉移到車廂2.2,然後掃描D,看D是否引用其它對象,如果引用了其它對象則也要轉移,依次類推。掃描完火車1的所有對象後,剩下的沒有轉移的對象都是垃圾,可以把整個火車1都作爲垃圾回收。注意如果在轉移時,如果車廂2.2空間滿了,則要在火車2末尾開闢新的車廂2.4,將新轉移的對象都放到2.4,即火車的尾部)

圖三

     補充說明:垃圾回收器一次只掃描一個車廂。圖三中的對象B與C並不是立即被回收,而是先會被轉移到火車1的尾部車廂。即掃描完1.1後,B被轉移到火車1 尾部,掃描完1.3後,C被轉移到車尾。等垃圾收集器掃描到火車1尾部時,如果仍然沒有外部對象引用它們,則B和C會被收集。

    火 車算法最大的好處是它可以保證大的循環結構可以被完全收集,因爲成爲垃圾的循環結構中的對象,無論多大,都會被移入同一列火車,最終一起被收集。還有一個 好處是這種算法在大多數情況下可以保證一次垃圾收集所耗時間在一定限度之內,因爲一次垃圾回收只收集一個車廂,而車廂的大小是有限度的。

發佈了82 篇原創文章 · 獲贊 76 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章