天天看點

JVM堆記憶體結構配置設定

JVM的堆記憶體分為新生代(Young Generation)和舊生代(Old Generation)。新生代分為Eden區和Survivor區。Survivor區分為From Survivor和To Survivor。如圖:

JVM堆記憶體結構配置設定
JVM堆記憶體結構配置設定
JVM堆記憶體結構配置設定
JVM堆記憶體結構配置設定

從上圖可以看出,新生代通常占JVM堆記憶體的1/3,因為新生代存儲都是新建立的對象,比較小的對象,而老年代存的都是比較大的,活的久的對象,是以老年代占JVM堆記憶體較大;還可以看出,新生代裡的Eden區通常占年輕代的4/5,兩個Survivor分别占新生代的1/10。因為Survivor中存儲的是GC之後幸存的對象,實際上隻有很少一部分會幸存,是以Survivor占的比例比較小。

下面講解一下當對象A建立之後,對象A在各個區之間的流轉過程,也就是各個區作用。

1) 對象A被new出來之後,是被存放在Eden區的。注釋:Eden即伊甸園,亞當和夏娃的故事

2) 當發生一次GC之後,Eden區存活下來的對象A會被複制到Survivor 1區(此時Survivor 1為To Survivor);Survivor 0 (此時為From Survivor)中存活的對象也會被複制到Survivor 1中。

3) GC會清空Eden和Survivor 0 (即From Survivor)中存儲的所有對象。因為Eden和Survivor 0 中存活的對象都被複制到 Survivor 1中了,是以清空是沒問題的。

4) 交換Survivor 0和Survivor 1的角色:即此時有資料的Survivor 1作為From Survivor,被清空的Survivor 0作為To Survivor。要保證在GC發生之前,To Survivor永遠是空的那個

5) 下次GC發生時,重複上述步驟。将Eden中存活的對象複制到To Survivor,将From Survivor中活的對象也複制到To Survivor。

說到此處,有細心的同學會發現,這都是新生代之間的作用,那老年代呢?

其實是這樣的,在上述步驟中,發生GC時,From Survivor中存活的對象并不是全部都會被複制到To Survivor中,而是根據這個對象在Survivor區中存活了多久而決定去向,當一個對象在Survivor中存活了很久(即經曆了多次GC還沒死),就會在發生GC時被複制到舊生代中。

一個很形象的例子描述對象在JVM堆記憶體中的生命周期:

我是一個普通的java對象,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間。有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“To”區,自從去了Survivor區,我就開始漂泊了,因為Survivor的兩個區總是交換名字,是以我總是搬家,搬到To Survivor居住,搬來搬去,居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。于是我就去了年老代那邊,年老代裡,人很多,并且年齡都挺大的,我在這裡也認識了很多人。在年老代裡,我生活了20年(每次GC加一歲),然後被回收。

還有要了解為什麼對象在新生代中複制來複制去的,而不是将死的直接清除,老的直接複制到舊生代?

這樣做的好處就是減少了記憶體碎片,而直接清除的話會使記憶體很零碎。詳情可以了解一下java垃圾回收算法中的複制算法。