Java垃圾回收機制

Java的內存分佈

在JVM中,內存是按照分代進行組織的。
算法

其中,堆內存分爲年輕代和年老代,非堆內存主要是Permanent區域,主要用於存儲一些類的元數據,常量池等信息。而年輕代又分爲兩種,一種是Eden區域,另一種是兩個大小對等的Survivor區域。之因此將Java內存按照分代進行組織,主要是基於這樣一個「弱假設」 - 大多數對象都在年輕時候死亡。同時,將內存按照分代進行組織,使得咱們能夠在不一樣的分代上使用不一樣的垃圾回收算法,使得整個內存的垃圾回收更加有效。多線程

年輕代的垃圾回收

在年輕代上採用的垃圾回收算法是「Mark-Copy」算法,並不一樣於咱們前面所瞭解的任何一種基本垃圾回收算法,可是Mark算法是同樣的,基於根對象找到全部的可達對象,具體可看Mark-Sweep算法中的Mark步驟. 而對於Copy算法,它僅僅是簡單的將符合必定年齡的對象從一個分代拷貝到另外一個分代。具體的回收過程以下:線程

首先,新對象的內存分配都是先在Eden區域中進行的,當Eden區域的空間不足於分配新對象時,就會觸發年輕代上的垃圾回收(發生在Eden和Survivor內存區域上),咱們稱之爲"minor garbage collection".同時,每一個對象都有一個「年齡」,這個年齡實際上指的就是該對象經歷過的minor gc的次數。如圖1所示,當對象剛分配到Eden區域時,對象的年齡爲「0」,當minor gc被觸發後,全部存活的對象(仍然可達對象)會被拷貝到其中一個Survivor區域,同時年齡增加爲「1」。並清除整個Eden內存區域中的非可達對象。對象

當第二次minor gc被觸發時(如圖2所示),JVM會經過Mark算法找出全部在Eden內存區域和Survivor1內存區域存活的對象,並將他們拷貝到新的Survivor2內存區域(這也就是爲何須要兩個大小同樣的Survivor區域的緣由),同時對象的年齡加1. 最後,清除全部在Eden內存區域和Survivor1內存區域的非可達對象。內存

當對象的年齡足夠大(這個年齡能夠經過JVM參數進行指定,這裏假定是2),當minor gc再次發生時,它會從Survivor內存區域中升級到年老代中,如圖3所示。get

其實,即便對象的年齡不夠大,可是Survivor內存區域中沒有足夠的空間來容納從Eden升級過來的對象時,也會有部分對象直接升級到Tenured內存區域中。it

年老代的垃圾回收

當minor gc發生時,又有對象從Survivor區域升級到Tenured區域,可是Tenured區域已經沒有空間容納新的對象了,那麼這個時候就會觸發年老代上的垃圾回收,咱們稱之爲"major garbage collection".io

而在年老代上選擇的垃圾回收算法則取決於JVM上採用的是什麼垃圾回收器。經過的垃圾回收器有兩種:Parallel Scavenge(PS) 和Concurrent Mark Sweep(CMS)。這兩種垃圾回收器的不一樣更多的是體如今年老代的垃圾回收過程當中,年輕代的垃圾回收過程在這兩種垃圾回收器中基本上是一致的。效率

就像其名字所表示的那樣,Parallel Scavenge垃圾回收器在執行垃圾回收時使用了多線程來一塊兒進行垃圾回收,這樣能夠提升垃圾回收的效率。而Concurrent Mark Sweep垃圾回收器在進行垃圾回收時,應用程序能夠同時運行。垃圾回收

Parallel Scavenge

PS垃圾回收器在年老代上採用的垃圾回收算法能夠看做是標記-清除算法標記-壓縮算法的結合體。

首先,PS垃圾回收器先是會在年老代上使用標記-清除算法來回收掉非可達對象所佔有的空間,可是咱們知道,標記清除算法的一個缺陷就是它會引發內存碎片問題。繼而有可能會引起連續的major gc。假設當前存在的內存碎片有10M,但最大的內存碎片只能容納2M的對象,這個時候若是有一個3M的對象從Survivor區域升級到Tenured區域,那Tenured區域也沒有辦法存放這個3M的對象。結果就是不斷的觸發major gc,直到Out of Memory。因此,PS垃圾回收器在清除非可達對象後,還會進行一次compact,來消除內存碎片。

Concurrent Mark Sweep

CMS垃圾收集器相比於PS垃圾收集器,它成功的減小了垃圾收集時暫停應用程序的時間,由於CMS在進行垃圾收集時,應用程序是能夠並行運行的。下面讓咱們來看看它是怎麼作到的。

從它的名字能夠看出,CMS垃圾收集器在年老代上採用的垃圾回收算法是標記-清除算法。可是,它跟標準的標記-清除算法略有不一樣。它主要分爲四個階段:

  1. Initial Mark階段 - 這個階段是Stop-The-World的,它會暫停應用程序的運行,可是在這裏階段,它不會標記出在Tenured區域中全部的可達對象。它只會從根對象開始出發,標記到根對象的第一層孩子節點即中止。而後恢復應用程序的運行。因此,這個暫停應用程序的時間是很短的。
  2. Concurrent Mark階段 - 在這個階段中,CMS垃圾回收器以Initial Mark階段標記的節點爲根對象,從新開始標記Tenured區域中的可達對象。固然,在這個階段中是不須要暫停應用程序的。這也是它稱爲"Concurrent Mark"的緣由。這同時也形成了一個問題,那就是因爲CMS垃圾回收器和應用程序同時運行,Concurrent Mark階段它並不保證在Tenured區域的可達對象都被標記了 - 應用程序一直在分配新對象。
  3. Remark階段 - 因爲Concurrent Mark階段它並不保證在Tenured區域的可達對象都被標記了,因此咱們須要再次暫停應用程序,確保全部的可達對象都被標記。爲了加快速度,這裏也採用了多線程來同時標記可達對象。
  4. Concurrent Sweep階段 - 最後,恢復應用程序的執行,同時CMS執行sweep,來清除全部非可達對象所佔用的內存空間。

從下圖能夠看到PS和CMS垃圾收集器的區別:

黑色箭頭表明應用程序的運行,綠色箭頭表明CMS垃圾收集器的運行。一根線條表示單線程,多個線條表示多線程。

因此,相比於PS垃圾收集器,CMS垃圾收集器成功的減小了應用程序暫時的時間。

Garbage First(G1)垃圾收集器

可是很不幸的是,CMS垃圾收集器雖然減小了暫停應用程序的運行時間,可是因爲它沒有Compact階段,它仍是存在着內存碎片問題。因而,爲了去除內存碎片問題,同時又保留CMS垃圾收集器低暫停時間的優勢,JAVA7發佈了一個新的垃圾收集器 - G1垃圾收集器。它會在將來逐步替換掉CMS垃圾收集器。

G1垃圾收集器和CMS垃圾收集器有幾點不一樣。首先,最大的不一樣是內存的組織方式變了。Eden,Survivor和Tenured等內存區域再也不是連續的了,而是變成了一個個大小同樣的region - 每一個region從1M到32M不等。

一個region有可能屬於Eden,Survivor或者Tenured內存區域。圖中的E表示該region屬於Eden內存區域,S表示屬於Survivor內存區域,T表示屬於Tenured內存區域。圖中空白的表示未使用的內存空間。G1垃圾收集器還增長了一種新的內存區域,叫作Humongous內存區域,如圖中的H塊。這種內存區域主要用於存儲大對象-即大小超過一個region大小的50%的對象。

在G1垃圾收集器中,年輕代的垃圾回收過程跟PS垃圾收集器和CMS垃圾收集器差很少,新對象的分配仍是在Eden region中,當全部Eden region的大小超過某個值時,觸發minor gc,回收Eden region和Survivor region上的非可達對象,同時升級存活的可達對象到對應的Survivor region和Tenured region上。對象從Survivor region升級到Tenured region依然是取決於對象的年齡。

對於年老代上的垃圾收集,G1垃圾收集器也分爲4個階段,基本跟CMS垃圾收集器同樣,但略有不一樣:

  1. Initial Mark階段 - 同CMS垃圾收集器的Initial Mark階段同樣,G1也須要暫停應用程序的執行,它會標記從根對象出發,在根對象的第一層孩子節點中標記全部可達的對象。可是G1的垃圾收集器的Initial Mark階段是跟minor gc一同發生的。也就是說,在G1中,你不用像在CMS那樣,單獨暫停應用程序的執行來運行Initial Mark階段,而是在G1觸發minor gc的時候一併將年老代上的Initial Mark給作了。
  2. Concurrent Mark階段 - 在這個階段G1作的事情跟CMS同樣。但G1同時還多作了一件事情,那就是,若是在Concurrent Mark階段中,發現哪些Tenured region中對象的存活率很小或者基本沒有對象存活,那麼G1就會在這個階段將其回收掉,而不用等到後面的clean up階段。這也是Garbage First名字的由來。同時,在該階段,G1會計算每一個 region的對象存活率,方便後面的clean up階段使用 。
  3. Remark階段 - 在這個階段G1作的事情跟CMS同樣, 可是採用的算法不一樣,G1採用一種叫作SATB(snapshot-at-the-begining)的算法可以在Remark階段更快的標記可達對象。
  4. Clean up/Copy階段 - 在G1中,沒有CMS中對應的Sweep階段。相反 它有一個Clean up/Copy階段,在這個階段中,G1會挑選出那些對象存活率低的region進行回收,這個階段也是和minor gc一同發生的,以下圖所示:

從上能夠看到,因爲Initial Mark階段和Clean up/Copy階段都是跟minor gc同時發生的,相比於CMS,G1暫停應用程序的時間更少,從而提升了垃圾回收的效率。

 

摘自:http://www.jianshu.com/p/778dd3848196

相關文章
相關標籤/搜索