六種主要的垃圾回收算法和思想

Java語言的一大特色就是能夠自動進行垃圾回收處理,無需開發人員過於關注系統資源的釋放狀況。自動垃圾收集雖然大大減輕了開發人員的工做量,可是也增長了軟件系統的負擔。一個不合適的垃圾回收方法和策略將會對系統性能形成不良影響。算法

1. 引用計數法

引用計數法是最經典古老的一種垃圾收集方法,它的實現也很簡單:對於一個對象A,只要有任何一個對象引用了A,則A的計數器就加1,當引用失效時,引用計數器就減1.只要對象A的引用計數器的值爲0,則對象A就不可能再被使用。性能

引用計數法實現簡單,只須要爲每個對象配備一個整型計數器便可。可是,它存在一個很嚴重的問題,即沒法處理循環引用的狀況,所以在Java的垃圾回收器中沒有使用這種算法。優化

一個簡單的循環引用示例以下:
線程

對象A和對象B循環引用,此時他們的引用計數器都不爲0,可是在系統中已經找不到第三個對象引用了A或者B,也就是說,A和B應該是被回收的垃圾。可是由於循環引用而沒法被識別,最終可能會致使內存泄漏。對象

2. 標記-清除法

標記-清除法是現代垃圾回收算法的基礎。內存

它將垃圾回收分爲兩個階段:標記階段和清除階段。一種可行的實現是:ssl

  1. 在標記階段,標記全部從根節點出發的可達對象。所以,全部未被標記的對象就是未被引用的垃圾對象。
  2. 在清除階段,清除全部未被標記的對象。

而標記-清除法可能產生的最大問題就是空間碎片。回收以後的空間不是連續的,不連續的內存空間的工做效率要低於連續的空間,這是標記-清除法最大的缺點。資源

3.複製算法

與標記-清除法相比,複製算法是一種相對高效的回收算法。開發

它的核心思想是:將內存分爲兩部分,每次只使用其中一部分。在垃圾回收時,將正在使用的內存中的存貨對象複製到未使用的內存塊中,以後清除正在使用的內存塊中的全部對象,交換兩個內存的角色,完成垃圾回收。效率

若是系統中的垃圾對象不少,那麼複製算法須要複製的存活對象的數量也不會不少,所以當須要使用複製算法時仍是比較高效的。又由於全部對象都會被統一複製到新的內存空間中,因此能夠保證回收後的內存空間是沒有碎片的。

雖然有以上兩大有點,可是複製算法的代價缺點是將系統內存摺半。所以單純的複製算法會讓人沒法接受。

在Java的新生代串行垃圾回收器中,使用了複製算法。新生代分爲eden空間、from空間和to空間三個部分。其中from和to空間能夠視爲用於複製的兩塊大小相同、地位相等,且角色能夠互換的空間塊,它們也被稱爲survivor空間,即倖存者空間,用於存放未被回收的對象。

在垃圾回收時,eden空間中的存活對象會被複制到未使用的survivor空間中(假設爲to),正在使用的survivor空間(假設爲from)中的年輕對象也會被複制到to空間中(大對象或者老年對象會直接進入老年代,若是to空間已經滿了,則對象也會直接進入老年代)。此時eden和from空間中的剩餘對象將都是垃圾對象,能夠直接清空,to空間則存放這次回收後存活的對象。

這樣改進後的複製算法既保證了空間的連續性,又避免大量內存空間被浪費。

4. 標記-壓縮算法

複製算法的高效性是創建在存活對象少,垃圾對象多的前提下。這種狀況廣泛存在於年輕代,可是在老年代,更常見的狀況是大部分對象都是存活對象,若是使用複製算法,因爲存活對象多,複製的成本也會很高。

基於老年代垃圾回收的特性,須要使用新的算法,而標記-壓縮算法是老年代的一種回收算法,它在標記-清除算法之上作了一些優化。

它和標記-清除算法不一樣之處在於:在清除階段,它會將全部的存活對象壓縮到內存的另外一端。以後清理邊界以外的全部空間。這種算法既避免了碎片的產生,又不須要兩塊相同的內存空間,所以性價比較高。

5. 增量算法

對大部分的垃圾回收算法而言,在垃圾回收的過程當中,應用軟件的全部線程都會掛起,暫停一切正常工做,等待來回收的完成。若是垃圾回收時間很長,則應用程序會被掛起好久,這會嚴重影響用戶體驗和系統穩定性。

增量算法的基本思想就是,讓垃圾回收線程和應用線程交替執行,每次只收集一小片區域的內存空間,接着切換應用程序線程。如此往復知道垃圾回收完成。

使用這種方式進行垃圾回收能夠減小系統的停頓時間,可是由於線程切換和上下文轉換的消耗,會使得垃圾回收的整體成本上升,形成系統吞吐量的降低。

6.分代

前面介紹的幾種垃圾回收算法沒有哪種能夠徹底替代其餘算法,它們有各自的優勢和缺點。所以,根據垃圾回收對象的特性,使用合適的算法回收,纔是明智的選擇。

分代就是基於這種思想,它將內存區域根據對象特色分爲幾塊,根據每塊內存區間的特色,使用不一樣的回收算法,提升垃圾回收的效率。

相關文章
相關標籤/搜索