在C++和Java之間隔著一堵由物件的記憶體手動分配釋放和自動回收圍成的牆,牆外面的人想進去,牆裡面的人卻想出來。C++和Java在對記憶體的管理上有著根本的區別。下面來講下Java的記憶體回收機制。

每種高階語言都有著自己相應的記憶體模型和回收機制。Java的記憶體是通過GC機制(Garbage Collection)來自動進行回收的,而GC機制是通過垃圾收集器(Garbage Collector)定時對記憶體(具體來說就是堆,當然也有很少的非堆的部分,主要針對堆來討論)中的死物件進行回收實現的。在jdk1.7版本中,共有7種垃圾回收器,分別是Serial,ParalleNew,Parralle Scavange,Serial Old ,Paralle Old ,CMS和 G1(具體請看官網說明)。

在具體介紹這些垃圾回收器之前,先講下JVM對記憶體的劃分。以HotSpot的JVM為例,JVM的堆可以劃分為,Young Generation和Tenured Generation,Younge Generation又分為Eden和Survivor區(其中Survivor又分為兩個相等大小的區域,即From和To),可由下面兩條公式來描述:

Heap= Younge Generation(Eden+Survivor)+Tenured Generation;;

Survivor = From +To;

Young Generation(新生代),顧名思義,是較新的物件所存在的堆中區域,Tenured Generation(老年代)是較老的物件所存在的區域。物件如果存活到一定年齡還未被回收的話,就會從Young Generation移到Tenured Generation,而無用物件則就會在Young Generation中被回收(當然,也有在Tenured Generation中被回收,但大部分情況下都是在Young Generation中被回收)。

HotSpot JVM 又把新生代進一步劃分為3個區域:一個相對大點的區域,稱為”伊甸園區(Eden)”;兩個相對小點的區域稱為”From 倖存區(survivor)”和”To 倖存區(survivor)”(From和To總是一樣大)。按照規定,新物件會首先分配在 Eden 中(如果新物件過大,會直接分配在老年代中)。在GC中,Eden 中的物件會被移動到survivor中,直至物件滿足一定的年紀(定義為熬過GC的次數),會被移動到老年代。

GC分為Minor GC(發生在新生代,代價小,通常幾十ms,頻繁發生)和Full GC(發生在老年代,代價大,通常在100ms到幾s之間,較少發生)。在一次Minor GC過程中,Eden和From區域存活的物件會被移動到To區,如果To滿了,那麼就會直接移動到老年代,如果連老年代都裝不來,那麼就會報OutOfMemory錯誤。但是有個值得注意的地方,也是很多部落格中沒講到的問題,From區和To區這兩個區域並不是固定的。每次Minor GC後,From區和To區相互對調名字。在下次GC時,To 倖存區會成為From 倖存區。


上圖演示GC過程,黃色表示死物件,綠色表示剩餘空間,紅色表示倖存物件

總結一下,物件一般出生在Eden區,年輕代GC過程中,物件在2個倖存區之間移動,如果物件存活到適當的年齡,會被移動到老年代。當物件在老年代死亡時,就需要更高級別的GC,更重量級的GC演算法(複製演算法不適用於老年代,因為沒有多餘的空間用於複製)

Ref:

官方gc collector說明:http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

官方的jvm引數配置:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

國外codecentric部落格:https://blog.codecentric.de/en/2012/08/useful-jvm-flags-part-5-young-generation-garbage-collection/