垃圾收集器與記憶體分配策略
物件存活判斷
參考計數演算法
- 給物件添加一個計數器,每有一個參考+1,當參考失效-1,若為0則不在被使用.
可達性分析演算法
-
物件是否可到達GC roots
或者說GC roots 是否是物件的上層節點(祖父節點,父節點)-
GC roots
- 虛擬機堆疊(堆疊中的本地變數表)中參考的物件
- 方法區中類靜態屬性參考的物件
- 方法區中常量參考的物件
- 本地方法堆疊中參考的物件
-
參考
-
如果reference型別的資料中存盤的數值代表另一塊的起始地址就稱這塊記憶體代表著一個參考(白話:堆疊中的reference保存的是一個記憶體起始地址)
-
強參考
- 永遠不會被回收,最常用的
-
軟參考
- SoftReference 在將要發生記憶體溢位前被標記,下次GC時回收
-
弱參考
- Weakreference每次GC都回收
-
虛參考
- PhantomReference 無法通過虛參考得到一個物件的實體,唯一目的,當回收時收到一個系統通知
-
生存與死亡
- 可達性分析--不可達-->標記---->篩選--1.是否覆寫了finalize();方法 2.finalize()是否被呼叫過--->(1.未覆寫 2.非首次呼叫) 沒必要執行
常見的垃圾收集演算法
標記-清除演算法
-
概念: 標記出需要回收的物件,統一回收標記物件
-
缺點
- 1.標記與清除效率都不高
- 2.清除后會有大量不連續的空間碎片,當分配記憶體需要的連續空間不足又會導致提前GC
復制演算法
-
概念:將記憶體分為兩塊(即為A,B),先在A中存盤,當A滿了將回收,回收后將保留物件復制到B,清空A,后續在B中存盤新的物件,B滿了.往復 (S0,S1的實作)
-
缺點
- 1.將記憶體一分為2,假設10M記憶體則無法保存5M以上 10M以下的物件
- 2.存活率高的物件會導致來回復制,消耗性能,效率變低
標記-整理演算法
- 概念:標記需要回收的物件,非標記物件像一端移動,清理端點邊界值后的記憶體
分代收集演算法
-
概念:根據物件存活周期的不同將記憶體劃分為不同的塊.(新生代,老年代),在根據每個塊的特性選擇最合適的收集演算法
-
新生代
-
物件存活率低,需要復制的存活物件少
- 復制演算法
-
-
老年代
-
物件存活率高,復制開銷大
- 標記-清理(整理)演算法
-
HotSpot的演算法實作
為了保持一致性,GC進行是必須停頓所有java執行執行緒,即 Stop The World
列舉根節點
- 在HotSpot的實作中,當類加載完成是,HotSpot將物件內偏移量上具體的資料計算出來,在特定的位置記錄下堆疊和暫存器中哪些位置是參考,使用一組稱為OopMap的資料結構,GC在掃描時就可直接掃描這些資訊,而不需要全量掃描
安全點 safePoint
-
程式執行并非任何地方都可停頓,只有當達到安全點時才能暫停.(Stop The World)
-
中斷方案
-
搶先中斷
- GC執行時,將所有執行緒中斷,若發現不在安全點中斷的執行緒就恢復這條執行緒,讓其運行至安全點. 幾乎已沒有虛擬機采用這種方式
-
主動中斷
- 不直接操作執行緒,而是對執行緒做標志,每個執行緒主動去輪詢這個標志,當發現這個中斷標志時自己中斷掛起.
-
安全區域 safe Region
-
在一段代碼片段中,參考關系不會發生變化,這個區域中任意地方開始GC都是安全的
-
執行緒--執行safe Region中代碼--> 標記自己已進入safeRegion---離開SafeRegion-->檢查系統是否完成跟節點列舉(或整個GC程序)
- 完成了執行緒繼續進行
- 未完成,等待,直到收到可以離開SafeRegion的信號為止
垃圾收集器
Serial
- JDK1.3.1以前,年輕代唯一的選擇
- 單執行緒收集器,必須暫停其它所有作業執行緒,直到它收集結束
- Client模式下默認的新生代收集器
- 優點:簡單高效,沒有執行緒互動開銷
ParNew
- 多執行緒版本的Serial
- Server模式下新生代首選收集器
- 優點:除了Serial以外,只有parNew可以與CMS配合作業
- -XX:+UseConcMarkSweepGC 默認新生代收集器
-XX:+UserParNewGC 強制指定
Parallel Scavenge
-
使用復制演算法,并行的多執行緒收集器
-
更關注于吞吐量,其它收集器關注縮短停頓時間(Stop The World)
- 吞吐量=運行用戶代碼時長/(運行用戶代碼時長+垃圾收集時長)
-
-XX:MaxGCPauseMillis 設定最大停頓時間(毫秒)
-XX:GCTimeRatio 設定吞吐量大小(0-100) -
-XX:UseAdaptiveSizePolicy
配合最大停頓時間,或吞吐量來使用.僅需要設定-Xmx-
GC自適應調節策略(與其它收集器的重要區別)
- 開啟這個引數將不在需要手工指定
-Xmn,
-XX:SurvivorRatio,
-XX:PretenureSizeThreshold等細節引數,虛擬機會根據當前系統運行情況進行調節
- 開啟這個引數將不在需要手工指定
-
Serial Old
-
單執行緒,老年代serial收集器,采用標記-整理演算法
- JDK1.5及以前與parallel Scavenge配合使用
- 作為CMS備選方案,當CMS發生 Concurrent Mode Failure時使用
Parallel Old
- JDK1.6開始提供
- Parallel Scavenge老年代版本,多執行緒,采用標記-整理演算法
- 注重吞吐量及CPU資源敏感時使用
CMS
-
HotSpot第一款并發收集器,以獲取最短回收停頓時間為目標的收集器
-
采用標記-清除演算法
-
運作流程
-
1.初始標記
- 僅標記GC roots能夠直接關聯到的物件,速度快(Stop the World) 單執行緒
-
2.并發標記
- 進行GC roots Tracing
-
3.重新標記
- 修正并發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分物件的標記記錄(Stop the World) 比初始標記時間長,比并發標記時間短 多執行緒
-
4.并發清除
-
-
缺點
-
對CPU資源非常敏感.默認啟動回收執行緒數(CPU數量+3)/4,4個以上的CPU則會占用25%以上資源.占用資源過多
-
無法處理浮動垃圾,可能因Concurrent Mode Failure而導致一次Full GC的觸發
- 浮動垃圾:并發時又產生的新垃圾
-
并發處理,則垃圾回收時需要預留記憶體供用戶執行緒使用,JDK1.5默認68%即會觸發GC,JDK1.6默認值為92%,
-XX:CMSInitiatingOccupancyFraction
來設定閥值 -
標記-清除演算法
- 提供了一個引數:
-XX:+UseCMSCompactAtFullCollection
默認開啟,FullGC后進行記憶體整理
-XX:CMSFullGCsBeforeCompaction
默認0即每次 設定多少次fullGC后整理一次記憶體
- 提供了一個引數:
-
G1
-
JDK7u4正式商用
-
G1新生代,老年代的區分與其它收集器模式不同,它將記憶體劃分成更多的Region,雖保留新生代,老年代概念,但已不在是物理隔離了,它們現在都是由Region(無需連續)組成的集合
-
特點
-
并行,并發
-
分代收集
-
空間整理
- 采用了標記-整理演算法
-
可預測的停頓
- G1將跟蹤每一個Region中垃圾堆積的價值大小.在后臺維護一個優先串列,每次根據允許的手機時間,優先回收價值最大的Region,以此來保證可預測的停頓時間,并達到最高的收集效率
-
-
在G1收集器中,有一個關鍵的概念,Remembered Set,每一個Region都一個對應的Remembered Set,每當程式對Reference型別資料進行了寫操作時,虛擬機會產生一個Write Barrier暫時中斷寫操作,檢查Reference參考的物件是否處于不同的Region中,如果是,則會通過CardTable將相關參考資訊記錄到被參考物件所屬的Region中的Remembered Set中.
當進行記憶體回收時,GC跟節點列舉范圍中加入Remembered Set 以保證不全堆掃描也不會遺漏 -
運作流程
-
初始標記
- 與CMS相同
-
并發標記
- 對堆中物件進行可達性分析,找出可達物件
-
最終標記
- 修正在并發標記期間用戶程式繼續運作而導致標記產生變動的那一部分物件的標記記錄,虛擬機將這些物件變化記錄在Remembered Set Logs中,并將Remembered Set Logs資料合并到Remembered Set中,停頓執行,但是可以并行執行
-
篩選回收
- 對每個Region的回收價值和成本進行排序,根據用戶指定的停頓時間來制定回收計劃,進行回收
-
記憶體分配及回收策略
進入Eden區,若不足觸發minor GC
大物件直接進入老年代
- 大物件:一般指大于Survivor一半以上
長期存活的物件進入老年代(即物件年齡達到閥值默認15)
當一組物件年齡相同的物件大于Survivor區一半時,年齡大于或等于這個年齡的物件都將直接進入老年代
空間分攤擔保
垃圾收集器常用引數
UseSerialGC
- 使用Serial+Serial Old 收集器
UseParNewGC
UseConcMarkSweepGC
- 使用parNew + CMS + Serial Old
UseParallelGC
UseParallelOldGC
SurvivorRatio
- 8:1
PretenureSizeThreshold
- 設定直接進入老年代物件的大小閥值
MaxTenuringThreshold
- 設定進入老年代的物件年齡.默認15
UseAdaptiveSizePolicy
- 由虛擬機動態調整java堆大小分配和進入老年代的物件年齡
HandlePromotionFailure
- 是否允許分配擔保失敗
ParallelGCThreads
- 設定并行GC時進行記憶體回收的執行緒數
GCTimeRatio
- 設定GC時間占比,默認99即允許GC占用1%時間
僅Parallel Scavenge 有效
MaxGCPauseMillis
- 最大停頓時間
僅Parallel Scavenge 有效
CMSInitiatingOccupancyFraction
- 設定GMS在老年代被占用X時啟動,默認68(JDK1.5) 92(JDK1.6)
UseCMSCompactAtFullCollection
- CMS收集后是否整理
CMSFullGCsBeforeCompaction

以上內容總結于《深入理解JAVA虛擬機》第二版 周志明 著
高清思維導圖版請關注公眾號‘伊人網路’ 回復 ‘收集器’ 即可領取

【著作權宣告】
本文著作權歸作者和博客園共有,歡迎轉載,未經作者同意必須在文章頁面給出原文鏈接,否則保留追究法律責任的權利,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/156817.html
標籤:Java
