1. 程式人生 > >java虛擬機器JVM--java虛擬機器的記憶體管理(新生代、老年代)

java虛擬機器JVM--java虛擬機器的記憶體管理(新生代、老年代)

前言

在上一篇部落格中,還遺留了一個問題:JVM的記憶體如何分配最高效?換一種說法就是:JVM的記憶體是如何的分配以及回收的?通過前面兩篇部落格的鋪墊:java虛擬機器JVM–java虛擬機器的結構, java虛擬機器JVM–java虛擬機器垃圾的回收機制詳解, 本篇將從JVM的記憶體如何分配的以及記憶體是如何回收的 角度來介紹java虛擬機器的記憶體管理,來回答這一個遺留下的的問題。

再貼一下JVM記憶體結構圖:

學java時應該看過這麼一句話:new 出來的物件都是在堆中的。事實上, 堆記憶體是JVM中最大的一塊記憶體, 而GC也是針對堆記憶體進行回收,所以下面我們將進入堆記憶體, 去看看堆記憶體的結構。

記憶體的分配及回收–堆中的新生代和老年代

java堆被分為兩部分, 一部分被稱為新生代, 一部分稱為老年代, 他們的比例通常為 1:2:

新生代

新物件被建立後一般來說都是進入新生代。新生代的特點是每次垃圾回收都需要回收大量的物件。JAVA中的物件大多數都是朝生夕死,所以很多物件建立後很快就沒用了,需要被回收,所以在新生代中使用了前一篇部落格介紹到的 複製演算法, 因為這樣是最高效的,所以我們把新生代又劃分為了三個區域,一個Eden區, 兩個Survivor區,比例為 8:1:1,這種比例提升了複製演算法的記憶體使用效率:

每次物件會存在於Eden區和一個Survivor區, 當記憶體不夠時,觸發GC,然後把依然存活的物件複製到另一個空白的Survivor區, 然後直接清空其餘新生代記憶體,然後如此迴圈。總有一個Survivor區是空白的。每進行一次GC,存活物件的年齡+1, 預設情況下,物件年齡達到15時,就會移動到老年代中。

老年代

老年代的特點是每次回收都只回收少量物件。當物件的年齡達到15就會存放到老年代,還有一種情況就是物件需要分配較大空間是,也會直接存放到老年代,從上面的圖也可看出, 老年代的空間是要比新生代大的。老年代空間大的原因在於, 老年代的物件都是生命週期比較長的, 會被引用的時間比較久,如果GC太頻繁,會嚴重影響效率,因為每次被回收的物件總是很少的, 確需要把整個老年代掃一次, 所以給老年代分配更多空間,減少GC回收的頻次,有利於提升效率。這就是為什麼新生代和老年代的比例為 1:2.

針對老年代的特點,採用的垃圾回收演算法是標記整理演算法, 就是將標記不回收的物件移動到一端,然後清除邊界以外的記憶體。

老年代的GC觸發,一般都伴隨著新生代的GC,因為新生代觸發一次GC,就可能有物件年齡大於15而移動到老年代, 導致老年代記憶體滿了。

永久代

在上圖中的方法區, 還畫了一個永久代。我們都知道方法區中(即永久代)存放類及方法的資訊、靜態常量等(在JDK1.7以前,不包括1.7)。但是類及方法的資訊等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢位,太大則容易導致老年代溢位。所以從JDK 1.7開始, 存在於永久代的常量池移動到了 堆區中,從JDK1.8開始,就沒有永久代了, 永久代被元空間的概念所替代。

元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過引數來指定元空間的大小。

到此,我們最開始的疑問:JVM的記憶體是如何的分配以及回收的? 就講完了~

喜歡的朋友,麻煩點個贊吧~