Java垃圾回收演算法

許佳佳233發表於2017-07-16

此篇文章主要借鑑《深入理解Java虛擬機器》和《Thinking in Java》

前提概要

Java垃圾回收機制主要有兩個方面,一是垃圾回收演算法,二是垃圾回收器。此篇文章主要介紹垃圾回收演算法。

主要的垃圾回收演算法如下:
1、引用計數演算法
2、標記-清掃演算法
3、複製演算法
4、標記-整理演算法
5、分代收集演算法

查詢可存活物件的方法

除了引用計數演算法外,其他的演算法都需要查詢可存活的物件,其方式主要如下:
1、使用遍歷的方式,遍歷“棧中的區域性變數的引用”和“方法區中的常量和靜態變數的引用”。
2、堆中被引用的物件就是存活的物件。

引用計數演算法

堆中的每個物件有一個引用計數器(初始值為0),當有引用連線物件的時候,引用計數器加1。垃圾回收器開始作用的時候,會在堆中遍歷,把引用計數為0的物件都釋放。

缺陷

無法釋放迴圈引用的物件。
比如有兩個類Student和Teacher。Student中有對Teacher的引用,而Teacher中也有對Student的引用。
分別新建了兩個物件s和t。s中有對t的引用,而t中有對s的引用。
那麼當我不使用s和t這兩個引用的時候,這兩個物件還是實際存在的,並且他們之間互相引用,所以無法釋放。

標記-清掃演算法

找出堆中所有存活的物件,每找到一個存活的物件,就會給物件設一個標記,這個過程不會回收任何物件。只有當全部標記工作完成的時候,清理動作才會開始。在清理過程中,沒有標記的物件會被釋放。

缺陷

所剩下的堆控制元件是不連續的,產生了大量不連續的記憶體碎片,空間碎片太多可能會導致在程式執行過程中要分配較大的物件的時候,無法找到足夠的連續記憶體而不得不提前出發垃圾收集動作。

複製演算法

將堆的容量劃分為大小相等的兩塊,每次只使用其中的一塊。當執行垃圾回收的時候,就把當前堆中存活的物件複製到另一個堆中,然後清空這個使用過的堆,這樣就只保留了存活的物件。
使用複製演算法就不需要考慮記憶體碎片的情況,只要移動堆中的指標,按順序分配記憶體即可,實現簡單,執行高效。

缺陷

1、代價很大,相當於將可以使用的記憶體縮小為了原來的一半。
2、如果觸發垃圾收集操作的時候,堆中只有少量的垃圾,甚至沒有垃圾。複製操作還是會把所有存活的物件都複製一遍,這會浪費資源。

標記-整理演算法

找出堆中所有存活的物件,每找到一個存活的物件,就會給物件設一個標記。讓所有存活的物件移動到記憶體的一端,然後直接回收掉端邊界以外的其他物件。(移動的過程不回收物件)

分代收集演算法

根據物件的存活週期不同將記憶體劃分為新生代和老年代,存活週期短的為新生代,存活週期長的為老年代。這樣就可以根據每塊記憶體的特點採用最適當的收集演算法。
新生代的中每次垃圾收集中會發現有大批物件死區,只有少量存活,那就選用複製演算法,只需要付出少量存活物件的複製成本就可以完成收集。
老年代中因為物件的存活率高,沒有額外的控制元件對它進行分配擔保,就必須使用“標記-清掃”或者“標記-整理”演算法來進行回收。

相關文章