垃圾回收算法與思想

引用計數法(Reference Counting):算法

引用計數法是最經典也是最古老的的一種垃圾收集算法,大體思想是:爲每個對象配備計數器,若是有引用就+1,若是引用失效就-1,若是某個對象的計數器爲0,說明沒有引用,則能夠被回收。優化

可是引用計數法有一個顯著的缺點就是沒法回收循環引用的對象,例若有A和B兩個對象,二者相互調用,可是這兩個對象除了相互調用外,沒有被其餘任何一個對象調用,可是由於相互調用,計數器不爲0,因此不會被回收,這就可能會致使內存泄漏。spa


標記-清除算法(Mark-Sweep):線程

標記-清除算法是現代垃圾回收算法的基礎思想,標記-清除算法將垃圾回收分爲兩個階段,一個是標記階段,另一個是清楚階段,對象

標記階段:從根節點開始,標記全部從跟節點到可達到的對象,未標記的對象就是沒有別引用的對象。生命週期

清除階段:清除全部未被標記的對象。內存

標記-清除算法最大的問題就是空間碎片,垃圾回收以後,空間是不連續的,在對象的堆空間分配過程當中,尤爲是大對象,不連續的內存空間的工做效率低於連續的。rem


複製算法(Copying)io

複製算法是一種相對比較高效的算法,主要思想是:將內存空間分爲大小相等的兩部分,每次只是用一塊,在垃圾回收的時候,將正在是用的那一塊空間中的引用對象複製到另一塊中,而後清除正在使用的那一塊空間的全部對象,交換兩個內存的角色,完成垃圾回收。效率

優勢是回收事後的內存空間沒有碎片。

缺點是內存摺半,複製大量的對象。

Java的新生代串行垃圾回收器用的就是這個算法。新生代分爲eden,from space, to space三個部分,from space 和 to space是大小相等,地位相等,可進行角色互換的空間塊。

在垃圾回收時,eden中的存活對象會被複制到未使用的survivor中(假設是to space),正在使用的存活對象也會從from space複製到to space中,eden 和 from space留下來的是未被使用的對象,被直接清空,to space存放複製事後的存活對象。


標記-壓縮算法(Mark-Compact)

複製算法的高效性是創建在存活對象少,垃圾對象多的狀況,如新生代。可是若是是老年代的話,複製成本太高,則就不適用了。

標記-壓縮算法是一種老年代的回收算法,它是在標記-清除算法的基礎上作了一些優化,如標記-清除同樣,首先也是從根節點開始,對全部可達對象進行標記,可是不一樣的是,標記-壓縮會將存活對象壓縮到內存的一端,而後清理邊界外的全部空間,這樣的好處是不用兩塊內存相同的空間和避免了空間碎片。


增量算法(Incremental Collecting)

對大部分垃圾回收算法而言,在進行垃圾回收期間,咱們的應用程序是處於掛起的狀態,也稱之爲Stop the World狀態,可是若是垃圾回收的時間過長的話,則應用程序掛起時間會好久,這樣會嚴重影響用戶體驗和系統的穩定性。

增量算法的基本思想是,若是一次性將全部的垃圾進行回收,須要形成系統長時間的停頓,那麼就能夠想到一個折中的辦法,讓垃圾回收線程和應用程序線程交替運行,垃圾回收線程只回收一小部份內存,而後就切換到應用程序線程,如此反覆,直到執行完垃圾回收。在垃圾回收過程當中,還執行了應用程序的代碼,這樣減小了系統停頓的時間,可是由於垃圾回收線程和上下文線程的切換的消耗,形成了垃圾回收成本的增長,從而致使了系統吞吐量的降低。


分代算法(Generational Collecting)

標記-清除,複製,標記-壓縮等算法,在這些算法中,並無一種算法徹底代替其餘算法,它們都就有各自的優點和特色。

所以,根據回收對象的特色,選擇合適的回收算法,纔是明智的選擇。

分代就是基於這樣的思想,它將內存區間根據對象的特色進行劃分,而後根據各個內存區間的特色,使用不一樣的垃圾回收算法,提升垃圾回收效率。

以Hot Spot爲例,它將全部新建的對象都放入稱爲新生代的內存區域,新生代的特色是對象朝生夕滅,大約90%對象都會被回收,所以新生代就適用用高效的複製算法;通過幾回垃圾回收依然存活的對象,就會保存在老年代中,老年代中幾乎全部的對象都是通過幾回垃圾回收依然倖存的,在一段時間內,甚至應用程序整個生命週期,都是內存常駐的。對於老年代,由於大部分對象是存活的,因此採用複製算法的話,是不可取的,採用標記-壓縮算法纔是高效的。

相關文章
相關標籤/搜索