title: jvm垃圾收集器與內存分配策略
date: 2017-12-13 23:56:27
tags: jvm
categories: jvm
垃圾收集器與內存分配策略
垃圾回收算法
引用計數算法:
給對象添加一個引用計數器,每當有一個地方引用時就加1,當引用失效就減1。當引用爲0時會被回收。
可達性分析算法:
通過一系列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索走過的路作爲引用鏈,當一個對象沒有任何引用鏈相聯,則該對象不可達,即爲可回收的對象。
GC-Roots包括:
- 虛擬機棧(棧幀中本地變量表)中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中JNI(一般爲Native方法)引用的對象
標記-清除算法:
首先標記所有需要回收的對象,在標記完成後統一回收所有被標記的對象
不足之處:
- 標記和清除兩個過程效率都不高
- 結束後會產生大量碎片
複製算法:
把內存劃分爲大小相等的兩塊,每次使用其中一塊,使用的那塊發生GC時將存活的對象複製到另一塊,每次只對一個半區進行回收。
不足:將內存縮小爲原來的一半,浪費空間
標記-整理算法:
標記過程與“標記-清除算法”相同,然後將對象碎片都向一邊移動,合成一個整體
再談引用
強引用:
普遍存在,即Object obj = new Object(); 只要強引用還在對象就不會被回收
軟引用:
描述一些有用但卻非必需的對象,在發生內存溢出異常之前會被回收。
弱引用:
描述非必需對象,比軟引用弱一些,在發生GC之前會被回收。
虛引用:
最弱的引用,對於對象的生存週期無影響,也無法通過虛引用來獲得對象實例。只是在對象被回收時會收到通知。
垃圾收集器
Serial收集器
單線程收集器,在GC時需要STW,即“Stop The World”暫停其他所有工作線程。缺點顯而易見,停頓長體驗差。唯一的優點是沒有多餘的線程開銷,最高的單線程收集效率,對於分配內存較小的桌面應用(Client)停頓一般控制在幾十毫秒,這是一個很好的選擇。
ParNew收集器
Serial的多線程版本,是許多運行在server模式下jvm首選的新生代收集器,而且除了Serial只有ParNew能與CMS收集器配合工作
並行:多個處理器上多個任務
併發:一個處理器上多個任務
Parallel Scavenge收集器
多線程使用複製算法的新生代收集器,關注點與CMS等收集器儘可能縮短停頓時間不同,其目標是達到一個可控制的吞吐量(吞吐量=運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)),稱爲吞吐量優先收集器。
Serial Old收集器
Serial老年代版本,單線程收集器,使用“標記-整理算法”。
Parallel Old收集器
Parallel Scavenge的老年代版本,使用多線程和“標記-整理”算法。
Parallel Scavenge + Parallel Old真正的吞吐量優先收集器。
CMS收集器
基於“標記-清除”算法,過程分爲四步:
- 初始標記
- 併發標記
- 重新標記
- 併發清除
其中初始標記和重新標記需要STW
缺點有三:
- 對CPU資源敏感,併發標記時用戶線程沒有暫停,收集器佔用部分CPU資源,可能會使用戶程序反應變慢
- 無法處理浮動垃圾,由於GC時用戶線程還在進行,不停地會有新的垃圾產生,只能在下一次GC時處理,這部分垃圾叫做浮動垃圾
- 基於“標記-清除”算法,會產生大量空間碎片,導致無法找到足夠大的連續空間分配而提前GC
G1收集器
運作步驟:
- 初始標記
- 併發標記
- 最終標記
- 篩選回收
特點:
- 並行與併發
- 分代收集(仍然保留了分代的概念)
- 空間整合(整體上屬於“標記-整理”算法,不會導致空間碎片)
- 可預測的停頓(比CMS更先進的地方在於能讓使用者明確指定時間片段內,消耗在垃圾收集上的時間)
內存分配策略
對象優先在Eden(年輕代)分配
新生代GC(Mirror GC):Java對象大部分都具備朝生夕滅的特性,Mirror GC非常頻繁
老年代GC(Full GC):出現Full GC常伴隨着Mirror GC,比Mirror GC慢十倍以上
大對象直接進入老年代
超過參數的對象直接在老年代分配
長期存活的對象將進入老年代
虛擬機給每個對象定義一個對象年齡計數器,當對象在Eden出生並且熬過第一次GC進入Survivor空間,那麼它的年齡即設爲1,每熬過一次GC年齡就加1,達到閾值就會晉升到老年代中
對象年齡判斷
然而也並不是一定要到達閾值才能晉升,如果在Survivor空間相同年齡的對象的總和大於Survivor空間的一半,大於等於該年齡的對象即可直接進入老年代
空間分配擔保
Mirror GC之前虛擬機會檢查老年代可用空間是否大於新生代所有對象大小總和,如果是,那麼可以確保Mirror GC安全。如果不是的話,看是否允許擔保失敗(允許冒險),如果允許那麼將檢查老年代可用空間是否大於歷次晉升對象大小的平均值,如果是的話將進行一次Mirror GC,儘管有風險會失敗;如果小於那麼將進行Full GC