工作原理:為每個記憶體對象維護一個引用計數。
當有新的引用指向某對象時就將該對象的引用計數加一,當指向該對象的引用被銷毀時將該計數減一,當計數歸零時,就回收該對象所佔用的記憶體資源。
缺陷:在每次記憶體對象被引用或引用被銷毀的時候都必須修改引用計數,這類操作被稱為footprint。引用計數的footprint是很高的。這使得程式整體的效能受到比較大的影響。因此多數現代的程式語言都不適用引用計數作為垃圾收集的實現演算法。
另外,引用計數還有一個致命的缺陷,當程中出現序循環參考時,引用計數演算法無法檢測出來,被循環參考的記憶體對象就成了無法回收的記憶體。從而引起記憶體泄露。
舉例說明就是:
class A{ public B b; }class B{ public A a;}public class Main{ public static void main(String[] args){ A a = new A(); B b = new B(); a.b=b; b.a=a; }}
在函數的結尾,a和b的計數均為2
先撤銷a,然後a的計數為1,在等待b.a對a的引用的撤銷,也就是在等待b的撤銷
對於b來講,也是同理
兩個對象都在等待對方撤銷,所有這兩個資源均不能釋放
標記-清除(Mark-Sweep)
此演算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍曆整個堆,把未標記的對象清除。此演算法需要暫停整個應用,同時,會產生記憶體片段。
複製(Copying)
此演算法把記憶體空間劃為兩個相等的地區,每次只使用其中一個地區。記憶體回收時,遍曆當前使用地區,把正在使用中的對象複製到另外一個地區中。次演算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的記憶體整理,不過出現"片段"問題。當然,此演算法的缺點也是很明顯的,就是需要兩倍記憶體空間。
標記-整理(Mark-Compact)
此演算法結合了"標記-清除"和"複製"兩個演算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍曆整個堆,把清除未標記對象並且把存活對象"壓縮"到堆的其中一塊,按順序排放。此演算法避免了"標記-清除"的片段問題,同時也避免了"複製"演算法的空間問題。
增量收集(Incremental Collecting)
實施記憶體回收演算法,即:在應用進行的同時進行記憶體回收。不知道什麼原因JDK5.0中的收集器沒有使用這種演算法的。
分代(Generational Collecting)
基於對對象生命週期分析後得出的記憶體回收演算法。把對象分為年青代、年老代、持久代,對不同生命週期的對象使用不同的演算法(上述方式中的一個)進行回收。現在的記憶體回收行程(從J2SE1.2開始)都是使用此演算法的。