【Java虛擬機】 內存分配與回收策略

前置概念

逃逸分析

所謂逃逸分析,即在編譯期間分析對象的動態(tài)作用域,確定了對象的作用域,可以為其他優(yōu)化手段提供參考,從而提高Java程序的性能。

? 方法逃逸:當對象在方法中被定義后,被外部方法所引用,例如將對象作為參數(shù)傳遞到其他方法中;
? 線程逃逸:一個對象被外部線程訪問到,例如將對象賦值給其他線程可以訪問的變量;
在這里主要關注通過逃逸分析判斷對象是否發(fā)生逃逸,來決定是在堆上還是棧上分配內存,從而減少虛擬機的GC壓力。
-XX:+DoEscapeAnalysis -- 開啟逃逸分析
-XX:+PrintEscapeAnalysis -- 輸入逃逸分析結果

TLAB

Thread Local Allocation Buffer(TLAB),JVM為每個線程分配獨立的內存空間,減少多線程環(huán)境下的都在堆上分配內存引起的競爭關系。

? 當創(chuàng)建新的對象時,首先查詢TLAB是否也有足夠的空間進行分配;
? 有足夠空間,則直接在TLAB上分配;
? 沒有足夠空間則會嘗試擴大TLAB的大小,無法擴大則在堆上進行分配。 TLAB提高了對象分配效率減少了全局鎖的競爭和垃圾回收開銷,從而提高了系統(tǒng)的效率。
-XX:+UseTLAB: -- 啟用TLAB
-XX:+TLABSize:-- 設置TLAB的初始大小
-XX:+PrintTLAB:-- 查看 TLAB 的分配情況
-XX:TLABRefillWasteFraction:-- 設置維護進入TLAB空間的單個對象大?。ū壤担?br> -XX:TLABWasteTargetPercent:-- 設置TLAB空間所占用Eden空間的百分比大小 默認1%
-XX:ResizeTLAB:-- 自調整TLABRefillWasteFraction閾值。

分配過程

  • ? 對象開始創(chuàng)建時,首先會進行逃逸分析確定其作用域,判定是否發(fā)生逃逸。
    • ? 沒有逃逸,并且棧有足夠空間,則在棧上進行分配。(棧上分配快速且不用回收)。
  • ? 發(fā)生了逃逸,則會判斷是否是大對象(-XX:PretenureSizeThreshold參數(shù)設置大對象的閾值)。
    • ? 如果是大對象,則直接在老年代進行分配。
  • ? 不是大對象,則嘗試在TLAB進行分配;
    • ? TLAB空間足夠則直接分配,否則在Eden區(qū)進行分配
    • ? TLAB實際是Eden區(qū)的一部分,所以是否在TLAB分配,都會被年輕代回收。

回收策略

  • ? 棧上分配的對象不需要回收,在POP時,生命周期結束;
  • ? 對象的不斷創(chuàng)建,會導致Eden區(qū)空間減少,當Eden區(qū)空間不夠時觸發(fā)YGC,回收已經消亡的對象;
  • ? YGC后存活下來的對象進入Survivor區(qū)(年輕代復制算法),在YGC每存活一次,age加1
  • ? 當age增加到一定閾值(-XX:MAXTenuringThreshold參數(shù)設置閾值)后,該對象就會晉升到老年代;
  • ? 當老年代空間不足時,則會觸發(fā)Full GC,回收整個堆的內存。
    注:
  1. 當Survivor區(qū)相同年齡的所有對象大小總和大于Survivor區(qū)的一半時,年齡大于等于該年齡的對象直接可以晉升到老年代;
  2. 當YGC前,虛擬機會判斷老年代的剩余空間是否大于新生代總空間或者大于歷次平均存活對象大小,又或者是否開啟handlePromotionFailure,大于或者開啟,則正常進行YGC,否則進行FGC。

編程啟發(fā)

  • ? 正確的選擇垃圾回收器,根據場景設置合適的回收器參數(shù)(對象大小,age,ES比值等);
  • ? 大對象直接進入老年代:應該避免頻繁創(chuàng)建大對象,消耗老年代空間。特別是朝生夕滅的大對象。
  • ? 長期存活的對象進入老年代:避免寫長調用的場景。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容