JVM垃圾回收算法總結

標記 - 清除算法

  標記 - 清除算法顧名思義,就是有兩段過程:第一步先標記,從每一個root節點開始向下遍歷,當一個對象到root節點沒有線路可達時,會被標記爲可回收,然後統一掃描回收這些被標記的對象(一些資料上是寫標記存活的對象,然後清除其他對象,我也還沒搞明白哪個說法是正確的,然後問了一個大佬,說是都標記。。。)。這種算法簡單粗暴,但缺陷也很明顯:一是效率問題;二是內存浪費問題,我覺得也是最主要的問題,由於需要回收的對象再內存中的分佈並不是連續的,可能那一塊這一塊的,導致清除後會產生大量不連續的內存碎片,而每塊內存碎片可能又比較小,就會出現再新建一個較大對象時,每個單獨的內存碎片都不夠存放這個對象,但全部未使用的內存大小又足夠存放這個對象,即無法找到足夠的連續的內存,有可能導致又一次的垃圾收集或內存溢出。如下圖:

垃圾回收前:
在這裏插入圖片描述
垃圾回收後:
在這裏插入圖片描述
  可以看到,垃圾回收後產生了大量的內存碎片。適用於老年代的回收垃圾。

複製算法

  複製算法是將內存分爲兩塊等同大小的空間,分配內存時,只使用其中的一塊。當一塊中的內存不足時,將內存中存活的對象轉移到另一塊還未使用的內存,而原先的那塊就只剩下需要回收的對象,所以直接將這塊內存回收掉即可。這樣的好處是將對象移動到另一塊內存區域時可以按順序的給對象分配空間,剩下的空間就是一大塊未分配的空間。如下圖:

垃圾回收前:
在這裏插入圖片描述
垃圾回收後:
在這裏插入圖片描述
  可以看到,複製算法已經不存在內存碎片問題了,但是如果將內存分爲兩塊等同大小的空間,則永遠只有一半的內存可以使用,內存使用率不高,也是浪費,所以可以改進一下。

  由於新生代中大部分對象的存活時間都很短,也就是說最終移動到另一塊內存的對象只是很小一部分,所以沒有必要將內存按1:1比例劃分,而是將內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次只使用Eden和其中一塊Survivor空間。垃圾回收時,只需將很小一部分對象轉移到另外一塊Survivor空間上,然後清理掉Eden和之前用過的Survivor即可。虛擬機默認的劃分比例是8:1:1,也就是說新生代每次可使用的內存最多佔總內存的90%,一下子就將之前的使用率從50%提高到90%。適用於新生代的回收垃圾。

標記 - 整理算法

  標記 - 整理算法整體思路與標記 - 清除算法差不多,第一步也是進行標記,將需要回收的對象標記出來,但第二步並不是直接清理了,而是將所有存活的對象移動到一端,然後直接清除掉端邊界的另一端。比如說可以將存活的對象都移動到上端(只是以這個圖爲例說明):

垃圾回收前:
在這裏插入圖片描述
對象移動後:
在這裏插入圖片描述
垃圾回收後:
在這裏插入圖片描述
  可以看到,這種算法不會產生內存碎片,也不會浪費內存空間。適用於老年代的回收垃圾。

分代收集算法

  其實上面已經提到過了,內存根據存活時間的不同,分爲了新生代和老年代,然後根據各自的特點,選用不同的垃圾收集算法。新生代一般選用複製算法,而老年代一般用標記 - 清理算法或標記 - 整理算法。


參考資料

  《深入理解Java虛擬機》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章