1. 程式人生 > >記憶體分配與回收策略

記憶體分配與回收策略

物件的記憶體分配,往大方向上講,就是在堆上分配(但也可能經過JIT編譯後被拆散為標量型別並間接的在棧上分配),物件主要分配在新生代的Eden區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB上分配。少數情況下也可能會直接分配在老年代中,分配的規則並不是百分之百固定的,其細節取決於當前使用的是哪一種垃圾收集器組合,還有虛擬機器中與記憶體相關的引數設定。

幾種記憶體分配機制:

這裡寫圖片描述

1.物件優先在Eden分配

大多數情況下,物件在新生代Eden區中分配,當Eden區沒有足夠的空間進行分配時,虛擬機器將發起一次Minor GC

-XX:SurvivorRatio = 8決定了新生代中Eden區與一個Survivor區的空間比例是8:1(因為Survivor會劃分為2塊即圖中S0和S1)

個人理解的分配過程:發現Eden區記憶體不足 ——> Minor GC ——> 已有物件放入Survivor區檢查能否放入 ——> 若Survivor空間也不足則只好通過分配擔保機制提前轉移到老年代中。
這裡寫圖片描述

Minor GC與Full GC的區別:

  • 新生代GC(Minor GC): 指的是發生在新生代的垃圾收集動作,因為Java物件大多數都具備朝生夕滅的特性,所以Minor GC非常頻繁,一般回收速度也比較快。
  • 老年代GC(Major GC / Full GC): 指的是老年代的GC,出現了Major GC,經常會伴隨至少一次的Minor GC(但並不是絕對的,比如在Parallel Scavenge收集器的收集策略裡有直接進行Major GC的策略選擇過程)。Major GC的速度一般比Minor GC的速度慢10倍以上。

2.大物件直接進入老年代

所謂的大物件是指,需要大量連續的記憶體空間的java物件,最典型的大物件就是那種很長的字串和陣列。

-XX:PretenureSizeThreshold引數令大於這個設定值的物件直接在老年代分配,避免在Eden區及兩個Surrivor區之間發生大量的記憶體拷貝,但這個引數只對Serial和ParNew兩款收集器有效,且單位是B。

3.長期存活的物件直接進入老年代

虛擬機器既然採用了分代收集的思想來管理記憶體,那記憶體回收時就必須能識別哪些物件應該放在新生代,哪些應放在老年代。
為了做到這點,虛擬機器給每個物件定義了一個物件年齡(Age)

計數器。
——–如果物件在Eden出生並經過第一次Minor GC後仍然存活,並且能被Survivor容納的話,將被移動到Survivor空間中,並將物件年齡設為1.物件在Survivor區中每熬過一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度(預設是15歲)時,就會被晉升到老年代中。物件晉升老年代的年齡閥值,可以通過引數-XX:MaxTenuningThreshold來設定。

4.動態物件年齡判定

如果在Survivor空間中相同年齡所有物件大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的物件就可以直接進入老年代,無需等到MaxTenuningThreshold中要求的年齡值。

5.空間分配擔保

在發生Minor GC時,虛擬機器會檢測之前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,如果大於則改為直接進行一次Full GC。如果小於則檢視HandlePromotionFailure設定是否允許擔保失敗,如果允許則只會進行Minor GC,如果不允許則也要改為進行一次Full GC。
這裡寫圖片描述