對象分配概述
-
G1提供了兩種分配策略java
- 基於線程本地分配緩衝區(Thread Local Allocation Buffer,TLAB)的快速分配
- 基於TLAB的慢速分配
- 慢速分配
- 當不能成功分配對象時就會觸發垃圾回收
對象分配流程圖
線程本地分配緩衝區(Thread Local Allocation Buffer,TLAB)
快速分配
指針碰撞法分配
- 若是TLAB剩餘空間(end - top) 大於當前對象待分配空間。則直接修改 top = top + objSize(對象大小);
- 若是TLAB滿了,則會保留這一部分空間,從新從堆內存中劃一片空間給TLAB
如何判斷TLAB滿了
- 虛擬機內部會維護一個 refill_waste,當請求對象大於refill_waste,會選擇在堆中分配,若小於該值,廢棄當前的TLAB,新建一個TLAB來分配該對象。
- refill_waste 可使用 TLABRefillWasteFraction 參數來調整,默認值爲64,即表示使用1/64的TLAB空間做爲refill_waste
- 假設 TLAB爲1M,那 refill_waste 爲16k,即當TLAB使用了1008k時(1024k - 16k),就直接分配一個新的,不然就儘可能使用這個老的TLAB
如何調整TLAB
- 除了 TLABRefillWasteFraction ,JVM還提供了參數TLAB WasteIncrement(默認值爲4個字),用於動態增長refill_waste
- refill_waste和TLAB的大小都會不斷動態調整,使系統狀態達到最優。
- 因爲大對象都不會在新生代中,TLAB都不能分配大對象,因此TLAB的大小不會大於HRSize/2。(大於HRSize/2就會被認爲是大對象)
TLAB中的慢速分配
- 若是TLAB中的剩餘空間很小(TLAB滿了),說明這個空間一般不知足對象分配,能夠直接丟棄,填充一個dummy對象,而後申請一個新的TLAB來分配對象。
- 若是TLAB剩餘空間比較多,那就不能丟棄TLAB,這時候就直接將對象分配到堆中,不使用TLAB,直接返回。
清理老的TLAB(dummy對象填充)
- GC在線性掃描堆的時候(好比查看HeapRegion對象,並行標記等),要知道哪裏有對象,哪裏是空白的。對於對象,掃描以後,能夠直接跳過這個對象的長度,若是是空白的,就須要一個字一個字的掃,效率很低。因此把TLAB的空白地方分配一個dummy對象(啞元對象),這樣GC就能作到快速遍歷了。
申請一個新的TLAB緩衝區
調用
G1CollectHeap中分配,主要是在
attempt_allocation中完成
日誌及解讀
經過命令設置參數,以下所示:性能
-Xmx128M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintTLAB -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest
能夠獲得:測試
garbage-first heap total 131072K, used 37569K [0x00000000f8000000,
0x00000000f8100400, 0x0000000100000000)
region size 1024K, 24 young (24576K), 0 survivors (0K)
TLAB: gc thread: 0x0000000059ade800 [id: 16540] desired_size: 491KB slow
allocs: 8 ref?ill waste: 7864B alloc: 0.99999 24576KB ref?ills: 50
waste 0.0% gc: 0B slow: 816B fast: 0Bd
分析日誌中TLAB這個信息的每個字段含義:
- desired_size:指望分配的TLAB的大小,這個值就是咱們前面提到如何計算TLABSize的方式。在這個例子中,第一次的時候,不知道會有多少線程,因此初始化爲1,desired_size=24576/50 = 491.5KB這個值是通過取整的。
- slow allocs:發生慢速分配的次數,日誌中顯示有8次分配到heap而沒有使用TLAB。
- refill waste:retire一個TLAB的閾值。
- alloc:該線程在堆分區分配的比例。
- refills:發生的次數,這裏是50,表示從上一次GC到此次GC期間,一共retire過50個TLAB塊,在每個TLAB塊retire的時候都會作一次ref?ill把還沒有使用的內存填充爲dummy對象。
-
waste:由3個部分組成:優化
- gc:發生GC時尚未使用的TLAB的空間。
- slow:產生新的TLAB時,舊的TLAB浪費的空間,這裏就是新生成50個TLAB,浪費了816個字節。
- fast:指的是在C1中,發生TLAB retire(產生新的TLAB)時,舊的TLAB浪費的空間。
慢速分配
這裏的慢速分配指的是在TLAB中分配,不能成功,最後進入慢速分配。(比TLAB慢速分配更慢
- attempt_allocation嘗試進行對象分配,若是成功則返回。
- 若是大對象,在attempt_allocatin_humongous分配,直接分配老年代.
- 若是分配不成功,則進行GC垃圾回收(主要是FullGC),而後再分配。
- 最終成功,或者嘗試N次後失敗,則分配失敗。
大對象分配流程(和TLAB相似,惟一區別就是對象大小不一樣)
- 嘗試垃圾回收(主要是增量回收,同時啓動併發標記)
-
嘗試開始分配對象spa
- 若是大於HRSize的一半且小於HRSize(即一個完整分區能夠保存):直接從空閒列表得到一個分區,或者分配一個新分區。
- 若是是大於HRSize,則是個連續對象,須要多個分區,思路同上,可是須要加鎖。
- 若是失敗再從嘗試垃圾回收開始。若是失敗達到必定次數,則分配失敗。
最後的分配嘗試
- 嘗試拓展新的分區,成功則返回
- 不成功進行則進行FullGC,可是不回收軟引用,再次分配成功則返回
- 不成功再次進行FullGC,回收軟引用,成功則返回
- 不成功則返回Null,分配失敗
G1垃圾回收的時機
- 分配內存時發現內存不足
-
外部顯式調用線程
-
java代碼中的system.gc()指針
- 若是設置了DisableExplicitGC(默認爲false),則不接受這個函數顯式觸發GC
- 默認爲FullGC,若是設置了ExplicitGCInvokesConcurrent,表示能夠進行併發的混合回收
-
和JNI交互,JNI代碼進入了臨界區
- 如JNI代碼爲了優化性能,提供了一個函數jni_GetPrimitiveArrayCritical/jni_GetStringCritical用於直接訪問原始內存數據,可是爲了保證安全必須使用GCLocker進行加鎖。當加鎖後發生了GC請求,此時GC會被延遲,直到GCLocker執行了unlock會從新補一個GC
- 若是設置了ExplicitGCInvokesConcurrent,表示能夠進行併發的混合回收,若是沒有設置,可能啓動新生代回收
參數介紹和調優
- 在優化調試TLAB的時候,在調試環境中能夠經過打開PrintTLAB來觀察TLAB分配和使用的狀況。
- 參數UseTLAB,指是否使用TLAB。大量的實驗能夠證實使用TLAB可以加速對象分配;該參數默認是打開的,不要關閉它。
- 參數ResizeTLAB,指是否容許TLAB大小動態調整。前面提到TLAB會進行動態化調整,主要是基於歷史信息(分配大小、線程數等),有基準測試代表使用動態調整TLAB大小效率更高。
- 參數MinTLABSize,指設置TLAB的最小值。實際應用須要設置該值,好比64K,通常能夠根據狀況設置和調整該值。
- 參數TLABSize,指設置TLAB的大小。實際中不要設置TLABSize,設置以後TLAB就不能動態調整了,即會使用一個固定大小的TLAB,前面咱們提到GC能夠根據狀況動態調整TLAB,在分配效率和內存碎片之間找到一個平衡點,若是設置該值則這種平衡就失效了。
- 參數TLABWasteTargetPercent,指的是TLAB可佔用的Eden空間的百分比,默認值是1。能夠根據狀況調整TLABWasteTargetPercent,增大則能夠分配更多的TLAB,3.1節中給出了具體的計算方式;另外若是實際中線程數目不少,建議增大該值,這樣每一個線程的TLAB不至於過小。
- 參數TLABRefillWasteFraction,指的是TLAB中浪費空間和TLAB塊的比例,默認值是64。能夠根據狀況調整TLABRef?illWasteFraction,主要考量點是內存碎片和分配效率的平衡,若是發現日誌waste中的slow和fast很大,說明浪費嚴重,能夠適當減小該參數值。
- 參數TLABWasteIncrement,指的是動態的增長浪費空間的字節數,默認值是4。增長該值會增長TLAB浪費的空間;通常不用設置。
- 參數GCLockerRetryAllocationCount默認值爲2,表示當分配中的垃圾回收次數超過這個閾值以後則直接失敗。
TLAB不是G1才引入的,對象分配是JVM提供的基礎分配功能,只不過G1結合本身內存分區的特徵,以及垃圾回收的具體實現,從新實現了分配的策略,重用了這些參數的功能和使用方法,且沒有引入額外的參數,因此這一部份內容不只適用於G1的調優,其餘的垃圾回收器一樣適用。