標記-清除算法,復制算法,標記-整理算法和分代算法


  最近筆者在學習JVM相關的知識,故寫作此文來分享一下本人了解的一些垃圾回收算法。

  

  標記回收算法

 


  最基礎的垃圾回收算法就是“標記-回收”算法(Mark-Sweep)算法,這是其它所有垃圾回收算法的基礎,相當於內功,其它算法都是根據內功來發揮的外功。顧名思義,本算法需要先標記出所有需要回收的對象,待標記完成后,再統一回收所有被標記的對象。它的缺點很明顯,首先是效率太低,標記和回收這兩個過程的效率都不算太高;其次,因為需要回收的對象不一定是連續的(實際上往往都不是),因此會產生很多內存碎片,一旦需要分配一個較大的空間給某個新對象的時候,很可能就會因為沒有足夠的連續內存,從而再運行一次gc機制,其實也是影響程序的運行效率。

  畫一張圖幫助理解一下對象占用內存的情況。

 

  復制算法


  CopyingGC算法是Marvin L.Minsky在1963年研究出來的算法,用來改進標記-回收算法。簡單的說,就是把內存划分為兩塊(兩塊大小不一定要相等,事實上,也不建議相等,下面會詳細說明),每次只使用其中的一塊,當內存空間被占用完以后,就把尚在存活的對象存到另外的半區,再將當前半區已使用的內存空間一次清除掉。這樣在分配內存時就不用考慮空間碎片,也不用使用空閑鏈表,避免了這類麻煩問題,只用移動堆頂指針,按順序分配內存即可,有效提高了效率。

  現代的商業虛擬機大多數都是采用了復制算法,只不過不是狹義上的復制算法而已,而是采用了多空間復制算法,因為傳統的復制算法會划出整個半區作為TO空間,這樣做的成本未免太高了。IBM公司的研究表明,新生代內的大部分對象都是朝生夕死的,所以沒有必要分配這么多內存空間去復制,而是將內存分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor空間,HotSpot虛擬機最初使用的就是這種內存分配方式。當需要回收內存的時候,就把Eden和Survivor中還存活着的對象一次性地復制到另一塊Survivor上,最后清理掉Eden和Survior,HotSpot默認的Eden和Survivor比例是8:1:1,就是說,每次能使用90%的內存容量。當然,也可能會出現剩余10%的Survivor空間不夠復制原有存活對象的情況,那就需要依賴其它內存(這里指老年代)進行分配擔保(Handle Promotion)。通過分配擔保機制,這些對象會直接進入老年代。實際上,說的再通俗一點,就是將Eden和一塊有內容的Survivor上存活的對象,復制到沒有內容的Survivor上,然后有內容的Survivor變成沒有內容的Survivor,沒有內容的Survivor變成有內容的Survivor。

 

  標記-整理算法

 


  雖然說復制算法效率很高,但這是在對象死亡很快的情況下,如果對象存活率較高,那就需要進行很多的復制操作,很影響效率。何況,還需要額外的空間進行分配擔保,因此,老年代不能采用這種算法,老年代采用的是復制-整理算法(Mark-Compact)。

  這種算法和標記-清除算法很像,唯一不同的是,當標記完成后,不是清理掉需要回收的對象,而是將所有存活的對象向一端移動,然后將邊界以外的內存全部清理掉,這樣可以有效避免空間碎片的產生。

 

  分代算法

 


  分代算法(Generational Collection),現代的虛擬機大都采用了這種gc回收算法,通過將內存根據存活時間的不同划分為不同代,來選擇最合適的對象回收算法,一般來說是分為新生代和老年代。新生代的對象更新很快,朝生夕死,所以使用復制算法,老年代的對象存活率很高,所以采用標記/壓縮算法。

 


  最后,總結一下在JVM里面所使用的GC回收算法。

      新生代串行收集/並行回收器:復制算法

      老年代串行收集/並行回收器:標記/壓縮算法

      並行收集器:將串行收集器多線程化,回收策略和串行收集器一致,因此該收集器是新生代為復制算法,老年代為標記-壓縮算法。

      CMS收集器:Concurrent Mark Sweep,從名字就可以看出來用的是標記清除算法

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM