一、分代GC的理論基礎
分代的垃圾回收策略,是基於這樣一個事實:不同的對象的生命周期是不一樣的。因此,不同生命周期的對象可以采取不同的收集方式,以便提高回收效率。
在Java程序運行的過程中,會產生大量的對象,其中有些對象是與業務信息相關,比如Http請求中的Session對象、線程、Socket連接,這類對象跟業務直接掛鈎,因此生命周期比較長。但是還有一些對象,主要是程序運行過程中生成的臨時變量,這些對象生命周期會比較短,比如:String對象,由於其不變類的特性,系統會產生大量的這些對象,有些對象甚至只用一次即可回收。
試想,在不進行對象存活時間區分的情況下,每次垃圾回收都是對整個堆空間進行回收,花費時間相對會長,同時,因為每次回收都需要遍歷所有存活對象,但實際上,對於生命周期長的對象而言,這種遍歷是沒有效果的,因為可能進行了很多次遍歷,但是他們依舊存在。因此,分代垃圾回收采用分治的思想,進行代的划分,把不同生命周期的對象放在不同代上,不同代上采用最適合它的垃圾回收方式進行回收。
二、代際划分
堆內存分為年輕代(Young Generation)和年老代(Old Generation)。年輕代又分為兩種,一種是Eden區域,另外一種是兩個大小對等的Survivor區域。
持久代即非堆內存,主要用於存儲一些類的元數據,常量池,java類,靜態文件等信息。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=<N>進行設置。
三、年輕代的垃圾回收
當新對象生成,並且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,並且把尚且存活的對象移動到Survivor區,然后整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。因為大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,所以Eden區的GC會頻繁進行。因而,一般在這里需要使用速度快、效率高的算法,使Eden區能盡快空閑出來。
首先,新對象的內存分配都是先在Eden區域中進行的,當Eden區域的空間不足於分配新對象時,就會觸發年輕代上的垃圾回收,我們稱之為"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所示。
四、年老代的垃圾回收
當minor gc發生時,又有對象從Survivor區域升級到Tenured區域,但是Tenured區域已經沒有空間容納新的對象了,那么這個時候就會觸發年老代上的垃圾回收,我們稱之為"major garbage collection"。而在年老代上選擇的垃圾回收算法則取決於JVM上采用的是什么垃圾回收器,通過的垃圾回收器有兩種:Parallel Scavenge(PS) 和Concurrent Mark Sweep(CMS)。
參考資料
http://www.jianshu.com/p/778dd3848196
http://itindex.net/detail/45308-jvm-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6-%E7%AD%96%E7%95%A5