1. 程式人生 > >深入理解java虛擬機器(二)

深入理解java虛擬機器(二)

前言

上篇已經介紹到記憶體結構劃分《深入理解java虛擬機器一》,本篇將介紹jvm記憶體分配。

正文

這裡主要介紹物件記憶體的分配,而物件是在堆中分配記憶體的,堆可以分為新年代和老年代,其中新年代可以劃分為Eden區和Survivor區,而Survivor又可以進一步劃分為From Surivor區和To Survivor區,簡稱S0和S1區。

大多數情況下,新生的物件都是在Eden區分配記憶體的,當Eden區沒有足夠空間的時候,虛擬機器就會發起一個Minor GC。

Minor GC:發生在新生代,新生代的生命週期比較短,因此Minor GC會頻繁的執行,執行速度快。

Full GC:發生在老年代,老年代裡面的物件生命週期都比較長,因此Minor GC不會頻繁的執行,執行速度要比Minor GC慢得多。

大致過程如下:

  1. 首次建立物件時,物件會進入到堆中的Eden區,當Eden區充滿物件的時候,就會發起一次Minor GC清理掉無用的物件(沒有被任何物件引用的物件),剩餘存活的物件會被轉移到Survivor區的S0區(或者是S1區)。
  2. 當Eden區再次充滿時,又會發起已Minor GC清理掉Eden區無用的物件,存活的物件的轉移到S1區,同時會清理掉S0區無用的物件,存活的物件的轉移到S1,最後清空S0區。
  3. 在Survivor去存活的物件的經過多次Minor GC之後會進入到老年代(HotSpot虛擬機器預設15次,用-XX:MaxTenuringThreshold控制)。
  4. 當發生Minor GC時,如果Survivor區記憶體不足,部分物件無法進入到Survivor區,虛擬機器會通過分配擔保機制提前將這些物件轉移到老年代。

大物件直接進入老年代

大物件是指需要大量的連續記憶體空間的物件,比如說大字元,陣列等,出現大物件時,容易提前觸發Minor GC來獲取足夠的連續空間來放置這些大物件。虛擬機器提供了一個-XX:PretenureSizeThreshold引數,令大於這個設定值的物件直接在老年代分配。避免在Eden區和兩個Survivor區之間發生大量的記憶體複製(新生代採用複製演算法進行GC)。

長期存活的物件進入到老年代

虛擬機器給每個物件定義了一個年齡計算器,新物件會進入Eden區,經過第一次Minor GC後,仍然存活且能夠被Survivor區接納的物件會被轉移到Survivor,並將物件的年齡設定為1,在Survivor中存活的物件沒經過一次Minor GC,年齡就會加1,當到達一定的年齡後就會晉級到老年代(預設是15)。可以通過引數-XX:MaxTenuringThreshold設定。

動態物件年齡判定

並不是所有的物件要等到年齡達到預設或者是指定的值得時候才會晉級到老年代,當Survivor中相同年齡的所有物件的總和達到Survivor空間一半,那麼大於或者等於該年齡的物件就可以直接晉級到老年代,無需等到年齡達到預設值或者是設定值。

空間分配擔保

在發起Minor GC之前,虛擬機器會先檢查老年代中可用的連續空間大小是否大於新生代所有物件總和,如果成立,那麼Minor GC確保是安全的,如果不成立,則虛擬機器會檢查HandlePromotionFailure設定值是否允許擔保失敗,如果允許,會繼續檢查老年代中最大可用的連續空間大小是否大於歷代晉升到老年代的物件的平均大小。如果大於,將進行一次Minor GC,但是這次GC是有風險的,因為Survivor無法容納的物件是無法確定的,只能根據每一次晉升到老年代物件大小的平均值老年代剩餘的空間做對比,當有大量物件建立時,就會發生記憶體溢位異常。如果說是小於的話,將進行一次Full GC來讓老年代騰出更多的記憶體空間。

本篇到此結束了,如果有問題,還望大佬們不吝賜教。

下篇將講解JVM垃圾回收機制,點選進入——深入理解java虛擬機器(三)