今天我關於常見的垃圾回收算法來做個總結,我們最常聽到的是Java虛擬機里的垃圾回收機制,其實垃圾回收的概念最先並不是Java里首先提出來的,
垃圾回收這個概念很早就已經被提出來了,並且已經在其他語言中得到了應用。
關於垃圾回收的機制,這里不再解釋,這篇文章我主要介紹常見的垃圾回收算法,當然還有其他的。
算法一:引用計數法。
這個方法是最經典點的一種方法。具體是對於對象設置一個引用計數器,每增加一個變量對它的引用,引用計數器就會加1,沒減少一個變量的引用,
引用計數器就會減1,只有當對象的引用計數器變成0時,該對象才會被回收。可見這個算法很簡單,但是簡單往往會存在很多問題,這里我列舉最明顯的兩個問題,
一是采用這種方法后,每次在增加變量引用和減少引用時都要進行加法或減法操作,如果頻繁操作對象的話,在一定程度上增加的系統的消耗。
二是這種方法無法處理循環引用的情況。再解釋下什么是循環引用,假設有兩個對象 A和B,A中引用了B對象,並且B中也引用了A對象,
那么這時兩個對象的引用計數器都不為0,但是由於存在相互引用導致無法垃圾回收A和 B,導致內存泄漏。
算法二:標記清除法。
這個方法是將垃圾回收分成了兩個階段:標記階段和清除階段。
在標記階段,通過跟對象,標記所有從跟節點開始的可達的對象,那么未標記的對象就是未被引用的垃圾對象。
在清除階段,清除掉所以的未被標記的對象。
這個方法的缺點是,垃圾回收后可能存在大量的磁盤碎片,准確的說是內存碎片。因為對象所占用的地址空間是固定的。對於這個算法還有改進的算法,就是我后面要說的算法三。
算法三:標記壓縮清除法(Java中老年代采用)。
在算法二的基礎上做了一個改進,可以說這個算法分為三個階段:標記階段,壓縮階段,清除階段。標記階段和清除階段不變,只不過增加了一個壓縮階段,就是在做完標記階段后,
將這些標記過的對象集中放到一起,確定開始和結束地址,比如全部放到開始處,這樣再去清除,將不會產生磁盤碎片。但是我們也要注意到幾個問題,壓縮階段占用了系統的消耗,
並且如果標記對象過多的話,損耗可能會很大,在標記對象相對較少的時候,效率較高。
算法四:復制算法(Java中新生代采用)。
核心思想是將內存空間分成兩塊,同一時刻只使用其中的一塊,在垃圾回收時將正在使用的內存中的存活的對象復制到未使用的內存中,然后清除正在使用的內存塊中所有的對象,
然后把未使用的內存塊變成正在使用的內存塊,把原來使用的內存塊變成未使用的內存塊。很明顯如果存活對象較多的話,算法效率會比較差,並且這樣會使內存的空間折半,但是這種方法也不會產生內存碎片。
算法五:分代法(Java堆采用)。
主要思想是根據對象的生命周期長短特點將其進行分塊,根據每塊內存區間的特點,使用不同的回收算法,從而提高垃圾回收的效率。
比如Java虛擬機中的堆就采用了這種方法分成了新生代和老年代。然后對於不同的代采用不同的垃圾回收算法。
新生代使用了復制算法,老年代使用了標記壓縮清除算法。
算法六:分區算法。
這種方法將整個空間划分成連續的不同的小區間,每個區間都獨立使用,獨立回收,好處是可以控制一次回收多少個小區間。
總結:各種回收算法都有各自的優缺點,沒有一種算法可以完全替代其他的算法,在具體的使用中應該結合具體的環境來選擇。