Java內存分配機制


內存分配,主要指的是在上的分配,

一般的,對象的內存分配都是在堆上進行,但現代技術也支持將對象拆成標量類型(標量類型即原子類型,表示單個值,可以是基本類型或String等),然后在棧上分配,在棧上分配的很少見,我們這里不考慮。

Java內存分配和回收的機制概括的說,就是:分代分配,分代回收

對象將根據存活的時間被分為:年輕代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法區)。

如下圖(來源於《成為JavaGC專家part I》,http://www.importnew.com/1993.html):

年輕代(Young Generation):對象被創建時,內存的分配首先發生在年輕代(大對象可以直接 被創建在年老代),大部分的對象在創建后很快就不再使用,因此很快變得不可達,於是被年輕代的GC機制清理掉(IBM的研究表明,98%的對象都是很快消 亡的),這個GC機制被稱為Minor GC或叫Young GC。注意,Minor GC並不代表年輕代內存不足,它事實上只表示在Eden區上的GC。

年輕代上的內存分配是這樣的,年輕代可以分為3個區域:Eden區(伊甸園,亞當和夏娃偷吃禁果生娃娃的地方,用來表示內存首次分配的區域,再 貼切不過)和兩個存活區(Survivor 0 、Survivor 1)。內存分配過程為(來源於《成為JavaGC專家part I》,http://www.importnew.com/1993.html):

 

  1. 絕大多數剛創建的對象會被分配在Eden區,其中的大多數對象很快就會消亡。Eden區是連續的內存空間,因此在其上分配內存極快;
  2. 當Eden區滿的時候,執行Minor GC,將消亡的對象清理掉,並將剩余的對象復制到一個存活區Survivor0(此時,Survivor1是空白的,兩個Survivor總有一個是空白的);
  3. 此后,每次Eden區滿了,就執行一次Minor GC,並將剩余的對象都添加到Survivor0;
  4. 當Survivor0也滿的時候,將其中仍然活着的對象直接復制到Survivor1,以后Eden區執行Minor GC后,就將剩余的對象添加Survivor1(此時,Survivor0是空白的)。
  5. 當兩個存活區切換了幾次(HotSpot虛擬機默認15次,用-XX:MaxTenuringThreshold控制,大於該值進入老年代)之后,仍然存活的對象(其實只有一小部分,比如,我們自己定義的對象),將被復制到老年代。

  從上面的過程可以看出,Eden區是連續的空間,且Survivor總有一個為空。經過一次GC和復制,一個Survivor中保存着當前還活 着的對象,而Eden區和另一個Survivor區的內容都不再需要了,可以直接清空,到下一次GC時,兩個Survivor的角色再互換。因此,這種方 式分配內存和清理內存的效率都極高,這種垃圾回收的方式就是著名的“停止-復制(Stop-and-copy)”清理法(將Eden區和一個Survivor中仍然存活的對象拷貝到另一個Survivor中),這不代表着停止復制清理法很高效,其實,它也只在這種情況下高效,如果在老年代采用停止復制,則挺悲劇的。

  在Eden區,HotSpot虛擬機使用了兩種技術來加快內存分配。分別是bump-the-pointer和TLAB(Thread- Local Allocation Buffers),這兩種技術的做法分別是:由於Eden區是連續的,因此bump-the-pointer技術的核心就是跟蹤最后創建的一個對象,在對 象創建時,只需要檢查最后一個對象后面是否有足夠的內存即可,從而大大加快內存分配速度;而對於TLAB技術是對於多線程而言的,將Eden區分為若干 段,每個線程使用獨立的一段,避免相互影響。TLAB結合bump-the-pointer技術,將保證每個線程都使用Eden區的一段,並快速的分配內 存。

  年老代(Old Generation):對象如果在年輕代存活了足夠長的時間而沒有被清理掉(即在幾次 Young GC后存活了下來),則會被復制到年老代,年老代的空間一般比年輕代大,能存放更多的對象,在年老代上發生的GC次數也比年輕代少。當年老代內存不足時, 將執行Major GC,也叫 Full GC。  

   可以使用-XX:+UseAdaptiveSizePolicy開關來控制是否采用動態控制策略,如果動態控制,則動態調整Java堆中各個區域的大小以及進入老年代的年齡。

  如果對象比較大(比如長字符串或大數組),Young空間不足,則大對象會直接分配到老年代上(大對象可能觸發提前GC,應少用,更應避免使用短命的大對象)。用-XX:PretenureSizeThreshold來控制直接升入老年代的對象大小,大於這個值的對象會直接分配在老年代上。

  可能存在年老代對象引用新生代對象的情況,如果需要執行Young GC,則可能需要查詢整個老年代以確定是否可以清理回收,這顯然是低效的。解決的方法是,年老代中維護一個512 byte的塊——”card table“,所有老年代對象引用新生代對象的記錄都記錄在這里。Young GC時,只要查這里即可,不用再去查全部老年代,因此性能大大提高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM