Java 的自動內存管理主要是針對對象內存的回收和對象內存的分配。同時,Java 自動內存管理最核心的功能是 堆 內存中對象的分配與回收。
JDK1.8之前的堆內存示意圖:
從上圖可以看出堆內存分為新生代、老年代和永久代。新生代又被進一步分為:Eden 區+Survivor1 區+Survivor2 區。值得注意的是,在 JDK 1.8中移除整個永久代,取而代之的是一個叫元空間(Metaspace)的區域(永久代使用的是JVM的堆內存空間,而元空間使用的是物理內存,直接受到本機的物理內存限制)。
1.對象優先在eden區分配
目前主流的垃圾收集器都會采用分代回收算法,因此需要將堆內存分為新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。
大多數情況下,對象在新生代中 eden 區分配。當 eden 區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC.下面我們來進行實際測試以下。
在測試之前我們先來看看 Minor GC和Full GC 有什么不同呢?
- 新生代GC(Minor GC):指發生新生代的的垃圾收集動作,Minor GC非常頻繁,回收速度一般也比較快。
- 老年代GC(Major GC/Full GC):指發生在老年代的GC,出現了Major GC經常會伴隨至少一次的Minor GC(并非絕對),Major GC的速度一般會比Minor GC的慢10倍以上。
2.大對象直接進入老年代
大對象就是需要大量連續內存空間的對象(比如:字符串、數組)。
為什么要這樣呢?
為了避免為大對象分配內存時由于分配擔保機制帶來的復制而降低效率。
3.長期存活的對象將進入老年代?
既然虛擬機采用了分代收集的思想來管理內存,那么內存回收時就必須能識別哪些對象應放在新生代,哪些對象應放在老年代中。為了做到這一點,虛擬機給每個對象一個對象年齡(Age)計數器。如果對象在 Eden 出生并經過第一次 Minor GC 后仍然能夠存活,并且能被 Survivor 容納的話,將被移動到 Survivor 空間中,并將對象年齡設為1.對象在 Survivor 中每熬過一次 MinorGC,年齡就增加1歲,當它的年齡增加到一定程度(默認為15歲),就會被晉升到老年代中。對象晉升到老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold
來設置。
4 動態對象年齡判定
為了更好的適應不同程序的內存情況,虛擬機不是永遠要求對象年齡必須達到了某個值才能進入老年代,如果 Survivor 空間中相同年齡所有對象大小的總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無需達到要求的年齡。
?