1. 程式人生 > >JVM中的新生代和老年代(Eden空間、兩個Survior空間)

JVM中的新生代和老年代(Eden空間、兩個Survior空間)

現有的主流JVM分別是HotSpot和JRockit,主要研究物件也是這兩個。這篇文章裡,我們只研究HotSpot,也就是所謂的Sun JVM。目前階段,Sun的GC方式主要有CMS和G1兩種。考慮到效果和實際應用,這裡只介紹CMS。CMS,全稱Concurrent Low Pause Collector,是JDK1.4後期版本開始引入的新GC演算法,在JDK5和JDK6中得到了進一步改進,它的主要適合場景是對響應時間的重要性需求較高的應用,並且預期這部分應用能夠承受垃圾回收執行緒和應用執行緒共享處理器資源,且應用中存在比較多的長生命週期的物件的應用。CMS是用於對Tenured Generation的回收,也就是年老代的回收,目標是儘量減少應用的暫停時間,減少Full GC發生的機率,利用和應用程式執行緒併發的垃圾回收執行緒來標記清除年老代。

JVM在程式執行過程當中,會建立大量的物件,這些物件,大部分是短週期的物件,小部分是長週期的物件,對於短週期的物件,需要頻繁地進行垃圾回收以保證無用物件儘早被釋放掉,對於長週期物件,則不需要頻率垃圾回收以確保無謂地垃圾掃描檢測。為解決這種矛盾,Sun JVM的記憶體管理採用分代的策略。

      1)年輕代(Young Gen):年輕代主要存放新建立的物件,記憶體大小相對會比較小,垃圾回收會比較頻繁。年輕代分成1個Eden Space和2個Suvivor Space(命名為A和B)。當物件在堆建立時,將進入年輕代的Eden Space。垃圾回收器進行垃圾回收時,掃描Eden Space和A Suvivor Space,如果物件仍然存活,則複製到B Suvivor Space,如果B Suvivor Space已經滿,則複製到Old Gen。同時,在掃描Suvivor Space時,如果物件已經經過了幾次的掃描仍然存活,JVM認為其為一個持久化物件,則將其移到Old Gen。掃描完畢後,JVM將Eden Space和A Suvivor Space清空,然後交換A和B的角色(即下次垃圾回收時會掃描Eden Space和B Suvivor Space。這麼做主要是為了減少記憶體碎片的產生。

我們可以看到:Young Gen垃圾回收時,採用將存活物件複製到到空的Suvivor Space的方式來確保儘量不存在記憶體碎片,採用空間換時間的方式來加速記憶體中不再被持有的物件儘快能夠得到回收。

      2)年老代(Tenured Gen):年老代主要存放JVM認為生命週期比較長的物件(經過幾次的Young Gen的垃圾回收後仍然存在),記憶體大小相對會比較大,垃圾回收也相對沒有那麼頻繁(譬如可能幾個小時一次)。年老代主要採用壓縮的方式來避免記憶體碎片(將存活物件移動到記憶體片的一邊,也就是記憶體整理)。當然,有些垃圾回收器(譬如CMS垃圾回收器)出於效率的原因,可能會不進行壓縮。

      3)持久代(Perm Gen):

持久代主要存放類定義、位元組碼和常量等很少會變更的資訊。

              

不過總的說來,Java的GC演算法感覺是業界最成熟的,目前很多其他語言或者框架也都支援GC了,但大多數都是隻達到Java Serial gc這種層面,甚至分generation都未考慮。JDK7裡面針對CMS進行了一種改進,會採用一種G1(Garbage-First Garbage Collection)的演算法。實際上Garbage-First paper(PDF) 2004年已經出現。

JVM區域總體分兩類,heap區和非heap區。heap區又分:Eden Space(伊甸園)、Survivor Space(倖存者區)、Tenured Gen(老年代-養老區)。 非heap區又分:Code Cache(程式碼快取區)、Perm Gen(永久代)、Jvm Stack(java虛擬機器棧)、Local Method Statck(本地方法棧)。

HotSpot虛擬機器GC演算法採用分代收集演算法:

1、一個人(物件)出來(new 出來)後會在Eden Space(伊甸園)無憂無慮的生活,直到GC到來打破了他們平靜的生活。GC會逐一問清楚每個物件的情況,有沒有錢(此物件的引用)啊,因為GC想賺錢呀,有錢的才可以敲詐嘛。然後富人就會進入Survivor Space(倖存者區),窮人的就直接kill掉。

2、並不是進入Survivor Space(倖存者區)後就保證人身是安全的,但至少可以活段時間。GC會定期(可以自定義)會對這些人進行敲詐,億萬富翁每次都給錢,GC很滿意,就讓其進入了Genured Gen(養老區)。萬元戶經不住幾次敲詐就沒錢了,GC看沒有啥價值啦,就直接kill掉了。

3、進入到養老區的人基本就可以保證人身安全啦,但是億萬富豪有的也會揮霍成窮光蛋,只要錢沒了,GC還是kill掉。

分割槽的目的:新生區由於物件產生的比較多並且大都是朝生夕滅的,所以直接採用標記-清理演算法。而養老區生命力很強,則採用複製演算法,針對不同情況使用不同演算法。

非heap區域中Perm Gen中放著類、方法的定義,JVM Stack區域放著方法引數、局域變數等的引用,方法執行順序按照棧的先入後出方式。

簡單來講,JVM的記憶體回收過程是這樣的:

物件在Eden Space建立,當Eden Space滿了的時候,gc就把所有在Eden Space中的物件掃描一次,把所有有效的物件複製到第一個Survivor Space,同時把無效的物件所佔用的空間釋放。當Eden Space再次變滿了的時候,就啟動移動程式把Eden Space中有效的物件複製到第二個Survivor Space,同時,也將第一個Survivor Space中的有效物件複製到第二個Survivor Space。如果填充到第二個Survivor Space中的有效物件被第一個Survivor Space或Eden Space中的物件引用,那麼這些物件就是長期存在的,此時這些物件將被複制到Permanent Generation。若垃圾收集器依據這種小幅度的調整收集不能騰出足夠的空間,就會執行Full GC,此時JVM GC停止所有在堆中執行的執行緒並執行清除動作。

1.為什麼會有年輕代

我們先來屢屢,為什麼需要把堆分代?不分代不能完成他所做的事情麼?其實不分代完全可以,分代的唯一理由就是優化GC效能。你先想想,如果沒有分代,那我們所有的物件都在一塊,GC的時候我們要找到哪些物件沒用,這樣就會對堆的所有區域進行掃描。而我們的很多物件都是朝生夕死的,如果分代的話,我們把新建立的物件放到某一地方,當GC的時候先把這塊存“朝生夕死”物件的區域進行回收,這樣就會騰出很大的空間出來。

2.年輕代中的GC

HotSpot JVM把年輕代分為了三部分:1個Eden區和2個Survivor區(分別叫from和to)。預設比例為8:1,為啥預設會是這個比例,接下來我們會聊到。一般情況下,新建立的物件都會被分配到Eden區(一些大物件特殊處理),這些物件經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。物件在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中。因為年輕代中的物件基本都是朝生夕死的(80%以上),所以在年輕代的垃圾回收演算法使用的是複製演算法,複製演算法的基本思想就是將記憶體分為兩塊,每次只用其中一塊,當這一塊記憶體用完,就將還活著的物件複製到另外一塊上面。複製演算法不會產生記憶體碎片。

在GC開始的時候,物件只會存在於Eden區和名為“From”的Survivor區,Survivor區“To”是空的。緊接著進行GC,Eden區中所有存活的物件都會被複制到“To”,而在“From”區中,仍存活的物件會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設定)的物件會被移動到年老代中,沒有達到閾值的物件會被複制到“To”區域。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名為To的Survivor區域是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有物件移動到年老代中。

      

3.一個物件的這一輩子

我是一個普通的Java物件,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間。有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“From”區,自從去了Survivor區,我就開始漂了,有時候在Survivor的“From”區,有時候在Survivor的“To”區,居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。於是我就去了年老代那邊,年老代裡,人很多,並且年齡都挺大的,我在這裡也認識了很多人。在年老代裡,我生活了20年(每次GC加一歲),然後被回收。

4.有關年輕代的JVM引數

1)-XX:NewSize和-XX:MaxNewSize

      用於設定年輕代的大小,建議設為整個堆大小的1/3或者1/4,兩個值設為一樣大。

2)-XX:SurvivorRatio

      用於設定Eden和其中一個Survivor的比值,這個值也比較重要。

3)-XX:+PrintTenuringDistribution

      這個引數用於顯示每次Minor GC時Survivor區中各個年齡段的物件的大小。

4).-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold

      用於設定晉升到老年代的物件年齡的最小值和最大值,每個物件在堅持過一次Minor GC之後,年齡就加1。

本文參考自:

http://lhc1986.iteye.com/blog/1421832

http://www.cnblogs.com/xhr8334/archive/2011/12/01/2270994.html

http://ifeve.com/jvm-yong-generation/