Java 虛擬機(四)垃圾收集算法

相關文章
Java虛擬機系列java

前言

在本系列上一篇文章中我講到了垃圾標記算法,垃圾被標記後,GC就會對垃圾進行收集,垃圾收集有不少種算法,這篇文章就來介紹經常使用的垃圾收集算法的思想。算法

1.標記-清除算法

標記-清除算法(Mark-Sweep)是一種常見的基礎垃圾收集算法,它將垃圾收集分爲兩個階段:微信

  • 標記階段:標記出能夠回收的對象。
  • 清除階段:回收被標記的對象所佔用的空間。

標記-清除算法之因此是基礎的,是由於後面講到的垃圾收集算法都是在此算法的基礎上進行改進的。標記-清除算法的執行的過程以下圖所示。
cdn

標記-清除算法主要有兩個缺點,一個是標記和清除的效率都不高,另外一個從上圖就能夠看出來,就是容易產生大量不連續的內存碎片,碎片太多可能會致使後續沒有足夠的連續內存分配給較大的對象,從而提早觸發新的一次垃圾收集動做。對象

2.複製算法

爲了解決標記-清除算法的效率不高的問題,產生了複製算法。它把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾收集時,遍歷當前使用的區域,把存活對象複製到另一個區域中,最後將當前使用的區域的可回收的對象進行回收。複製算法的執行過程以下圖所示。blog

這種算法每次都對整個半區進行內存回收,不須要考慮內存碎片的問題,代價就是使用內存爲原來的一半。
複製算法的效率跟存活對象的數目多少有很大的關係,若是存活對象不多,複製算法的效率就會很高。因爲絕大多數對象的生命週期很短,而且這些生命週期很短的對象都存於新生代中,因此複製算法被普遍應用於新生代中,關於新生代中複製算法的應用,會在後面的分代收集算法中詳細介紹。生命週期

3.標記-壓縮算法

在新生代中可使用複製算法,可是在老年代就不能選擇複製算法了,由於老年代的對象存活率會較高,這樣會有較多的複製操做,致使效率變低。標記-清除算法能夠應用在老年代中,可是它效率不高,在內存回收後容易產生大量內存碎片。所以就出現了一種標記-壓縮算法(Mark-Compact)算法,與標記-清除算法不一樣的是,在標記可回收的對象後將全部存活的對象壓縮到內存的一端,使他們緊湊的排列在一塊兒,而後對端邊界之外的內存進行回收。回收後,已用和未用的內存都各自一邊,以下圖所示。內存

標記-壓縮算法解決了標記-清除算法效率低和容易產生大量內存碎片的問題,它被普遍的應用於老年代中。get

4.分代收集算法

Java堆區的空間劃分

在Java虛擬機中,各類對象的生命週期會有着較大的差異,大部分對象生命週期很短暫,少部分對象生命週期很長,有的甚至和應用程序以及Java虛擬機的運行週期同樣長。所以,應該對不一樣生命週期的對象採起不一樣的收集策略,根據生命週期長短將它們分別放到不一樣的區域,並在不一樣的區域採用不一樣的收集算法,這就是分代的概念。
如今主流的Java虛擬機的垃圾收集器都採用分代收集算法(Generational Collection)。Java堆區基於分代的概念,分爲新生代(Young Generation)和老年代(Tenured Generation),其中新生代再細分爲Eden空間、From Survivor空間和To Survivor空間。由於Eden空間大多對象生命週期很短,因此新生代的空間劃分並非均分的,HotSpot虛擬機默認Eden空間和兩個Survivor空間的所佔的比例爲8:1。博客

分代收集

根據Java堆區的空間劃分,垃圾收集的類型分爲兩種,它們分別是:

  • Minor Collection:新生代垃圾收集。
  • Full Collection:對新生代、老年代和永久代(JDK8 取消永久代,Full Collection掃描不到替代永久代的元空間)進行收集,又能夠稱做Majjor Collection。它的收集頻率較低,耗時較長。

當執行一次Minor Collection時,Eden空間的存活對象會被複制到To Survivor空間,而且以前通過一次Minor Collection並在From Survivor空間存活的仍年輕的對象也會複製到To Survivor空間。
有兩種狀況Eden空間和From Survivor空間存活的對象不會複製到To Survivor空間,而是晉升到老年代。一種是存活的對象的分代年齡超過-XX:MaxTenuringThreshold(用於控制對象經歷多少次Minor GC才晉升到老年代)所指定的閾值。另外一種是To Survivor空間容量達到閾值。
當全部存活的對象被複制到To Survivor空間,或者晉升到老年代,也就意味着Eden空間和From Survivor空間剩下的都是可回收對象,以下圖所示。

這時GC執行Minor Collection,Eden空間和From Survivor空間都會被清空,而存活的對象都存放在To Survivor空間。
接下來將From Survivor空間和To Survivor空間互換位置,也就是此前的From Survivor空間成爲了如今的To Survivor空間,每次Survivor空間互換都要保證To Survivor空間是空的,這就是複製算法在新生代中的應用。在老年代則採用了標記-壓縮算法。
在HotSpot中,基於分代的概念,GC使用的回收算法針對新生代和老年代的特色,採用不一樣的垃圾收集算法。

參考資料
《深刻理解 Java 虛擬機:JVM 高級特性與最佳實踐》第二版
《Java虛擬機精講》
《HotSpot實戰》


歡迎關注個人微信公衆號,第一時間得到博客更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。

相關文章
相關標籤/搜索