1. 對象存活分析
1.1 引用計數法
原理:給對象添加一個引用計數器,每當有一個引用時,加1,當引用失效時,減1,即引用技術為0時代表對象不再會被使用
缺點:解決不了相互引用的問題
1.2 可達性分析法
原理:GC Roots為起點,向下搜索,能鏈接到某個對象時,即此對象時可用的
優點:解決了引用計數出現循環引用的問題
可作為GC Roots的對象包含:
虛擬機棧中引用的對象
方法區中類靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中JNI(即native方法)引用的對象
1.3 引用
強引用 Strong Reference
只要引用還在(類似 Object obj = new Object()),垃圾收集器就永遠都不會回收被引用的對象
軟引用 SoftReference
還有用但不是必需的,軟引用關聯的對象,在系統將要發生內存溢出時,將此類對象列進回收范圍進行二次回收,若內存還是不夠則拋出內存溢出異常
弱引用 WeakReference
描述非必需對象,弱引用關聯的對象只能活到下一次GC之前
虛引用 PhantomReference
虛引用也成為幽靈引用或者幻影引用,是最弱的一種引用關系,一個對象是否有虛引用的存在完全不會影響其存活時間,也無法通過虛引用來取得一個對象實例。為一個對象設置虛引用的唯一目的就是當對象被回收時收到一個系統通知。
1.4 沒有任何引用的對象有一次自救機會
在可達性分析中不可達的對象,并非是一定要被回收的,至少需要經歷兩次標記過程:如果對象在進行可達性分后發現沒有與 GC Roots 相連接的引用鏈,那么它將會被第一次標記并且進行一次篩選,篩選的條件是此對象是否有必要執行finalize()方法,當沒有覆蓋finalize()方法,或者finalize() 已經被虛擬機調用過,虛擬機將這兩種情況都視為“沒有必要執行”,對象將會被回收。
若是有必要執行,那么對象將會被放入F-Queue 的隊列中,并在稍后有一個虛擬機自動建立的,低優先級的Finalizer線程區執行它。稍后GC將對F-Queue中的對象進行二次標記,若在finalize()方法中成功自救(即與引用鏈上的任意對象關聯上),將其移出F-Queue,否則基本上會被真的回收了。
1.5 回收方法區
主要收集廢棄常量和無用的類
廢棄常量:與回收Java堆中的對象非常相似。以常量池中字面量的回收為例,若一個字符串進入池中,但是沒有任何一個對象引用此常量,如果此時發生內存回收,且有必要的話,此常量將會被回收。
無用的類,同時滿足一下三點:
該類所有的實例都已經被回收,即Java堆中不存在該類的任何實例
加載該類的ClassLoader已經被回收
該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法
2. 垃圾收集算法
2.1 標記-清除算法
原理:首先標記出所有需要回收的對象,再標記完成后統一回收所有被標記的對象
缺點:
- 效率低
- 會產生內存碎片,提前觸發GC
2.2 復制算法
原理:將內存分成兩塊,每次存活的對象復制進另一塊內存,然后再把已使用過的內存空間清理掉。
優點:實現簡單,效率高
缺點:內存利用率低,存活率較高時效率很低。
使用方式:調整第一塊內存與第二塊內存比率,如 Eden:Survivor=8:1,存活率較低時使用較好,如新生代
2.3 標記-整理算法
原理:首先標記出所有需要回收的對象,將存貨的對象都向一段移動,然后清理掉端邊界外的所有對象。
使用:存活率較高的情況,如老年代
2.4 分代收集算法
原理:根據分代的特點選擇所需算法。如新生代使用復制算法,老年代使用標記-整理算法。
3. GC算法實現(Hotspot)
3.1 枚舉根節點
3.2 安全點
- 搶先式中斷
- 主動式中斷
3.3 安全區域
4. 垃圾收集器
4.1 Serial收集器
原理: 不僅僅是指使用一個CPU或者一條線程去執行,并且在進行收集時,會暫停所有線程,知道他收集結束,即(Stop The World)
優勢: 簡單而高效,是虛擬機運行在Client模式下的很好的選擇
4.2 ParNew收集器
Serial收集器的多線程版本,能與CMS收集器配合工作
4.3 Parallel Scavenge收集器
目標是達到一個可控制的吞吐量,適合在后臺運算而不需要太多交互的任務。
-XX:MaxGCPauseMillis 最大垃圾收集停頓時間
-XX:GCTimeRatio 吞吐量大小
4.4 Serial Old收集器
Serial的老年代版本,使用“標記-整理”算法
4.5 Parallel Old收集器
Parallel Scavenge的老年代版本,使用多線程和“標記-整理”算法
4.6 CMS(Concurrent Mark Sweep)收集器
目標: 獲取最短回收停頓時間
優勢:并發收集,低停頓
缺點:1. 占用大約25%CPU資源 2.會產生浮動垃圾 3.內存空間碎片
使用: 跟用戶交互頻繁的場景
- 初始標記
標記 GC Roots 直接關聯到的對象 - 并發標記
進行 GC Roots過程 - 重新標記
修正并發標記階段因用戶繼續運作而導致標記產生變動的那一部分對象的標記記錄 - 并發清除
并發清理
4.7 G1收集器
并行與并發
分代收集
空間整合
可預測的停頓
5. 內存分配與回收策略
1.對象優先在Eden分配
大多數情況下,對象在新生代Eden中分配,當Eden區沒有足夠空間進行分配時,將進行一次MinorGC,若GC期間Survivor區間容量不夠,則提前進入老年代。
2.大對象直接進入老年代
大對象是指需要連續內存空間的JAVA對象,最典型的就是很長的字符串和數組。這種對象太頻繁會導致提前GC
-XX:PretenureSizeThreshold參數,令大于此值的對象直接進入老年代。這樣做可避免Eden區以及兩個Survivor區之間發生大量的復制。
3. 長期存活的對象進入老年代
既然采用分代思想來進行管理,那么內存回收時必需能識別那些處于新生代,哪些處于老年代,為此,虛擬機給每個對象引入一個對象年齡計數器。對象經過一次MinorGC仍然存活將進入Survivor區,此時年齡+1,在Survivor中熬過一次GC,年齡+1,當達到指定的年齡之后,將其晉升到老年代。
-XX:MaxTenuringThreshold
4. 動態對象年齡判斷
如果在Survivor中相同年齡的對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象將直接進入老年代,無需年齡達到閾值。
5. 空間分配擔保
在發生MinorGC之前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有對象總和,那么 Minor GC 可確保是安全的。否則
1.JDK6u24之前虛擬機會查看HandlePromotionFailure的值,允許擔保失敗,那么會繼續檢查老年代最大可用的連續空間是否大于歷次晉升老年代的平均大小,若大于,則嘗試進行一次 Minor GC,若擔保失敗,將進行一個 Full GC ; 若HandlePromotionFailure不允許擔保失敗,或者歷次平均值小于現有新生代總和,則不允許冒險,將進行一次 Full GC.
2.JDK6u24之后HandlePromotionFailure值不在有影響,只會依據老年代連續空間大小大于新生代總和歷次晉升平均大小,就進行Minor GC , 否則進行 Full GC。