總覽算法
本文會介紹垃圾回收的如下幾個方面。緩存
- 爲何要垃圾回收
- 在哪裏回收
- 哪些對象須要回收
- 怎麼回收
- HotSpotJVM中有哪些具體的回收器能夠直接用。
在開始講垃圾回收以前,先經過一張圖快速回憶一下運行時內存多線程
爲何須要垃圾回收併發
- 由於程序在運行的過程當中,對象實例,變量會佔據愈來愈多的內存,若是不及時的清理,會形成性能降低和內存耗盡的問題。
從哪裏回收性能
- 堆和方法區。堆裏面再也不使用的對象實例,方法區裏面的再也不使用的常量和類。
如何判斷一個對象須要回收呢?url
- 有兩種算法。
- 引用計數法,就是對象被引用一次就+1, 當引用爲0的時候就進行回收。
這種算法缺點是當兩個對象互相引用的時候,就不會被回收。spa
- 可達性分析法:經過對象是否可以經過GC ROOT的引用鏈可達 判斷 對象是否能夠被回收。
上圖就是一個可達性分析的常見場景,對象三何時回被回收?先上結果:對象三是軟可達,正常狀況下在內存不足的時候會被回收。.net
這裏須要理解兩個概念線程
-
- 一個是哪些數據能夠做爲GC ROOT,虛擬機棧中本地變量表引用的對象
- 方法區中
- 類靜態變量引用的對象
- 常量引用的對象
- 本地方法棧中JNI引用的對象
- 方法區中
- 一個是哪些數據能夠做爲GC ROOT,虛擬機棧中本地變量表引用的對象
-
- 一個是對象引用有哪幾種類型。
爲了更加靈活的控制對象的生命週期,將對象引用分爲四個級別。3d
-
-
- 強引用( Strong Reference ):這是最多見的引用,不會被GC,除非你顯示的置爲null,纔會GC。
-
Object o=new Object();
-
-
- 軟引用( Soft Reference ):用來描述有用但非必須的對象,內存不足的時候回GC,多用於caching/pooling中,好比下面例子網頁緩存吃緊的時候回收內存。
-
SoftReference<String> sr = new SoftReference<String>(new String("hello")); System.out.println(sr.get());
-
-
- 弱引用( Weak Reference ):也是用來描述非必須的對象,在下一次GC的時候就會回收,跟內存是否不足無關。
-
Object obj = new Object(); WeakReference<Object> WeakRef =new WeakReference(obj); Object objg = WeakRef.get();
-
-
- 虛引用( Phantom Reference ) : 不會影響對象的生命週期,也沒法經過虛引用獲取對象實例,僅用於在發生GC的時候接受一個系統通知。
- 必須和引用隊列一塊兒使用,主要用來判斷對象是否將被垃圾回收器回收。
- 垃圾回收器準備回收虛引用對象時,會將這個引用加入到與之關聯的引用隊列中,你經過引用隊列判斷到某個引用將要被回收就能夠當即採起行動。
- 虛引用( Phantom Reference ) : 不會影響對象的生命週期,也沒法經過虛引用獲取對象實例,僅用於在發生GC的時候接受一個系統通知。
-
ReferenceQueue<String> queue = new ReferenceQueue<String>(); PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue); System.out.println(pr.get());
理解了上面兩個概念以後,咱們在思考下面一個問題,若是一個對象的引用有多個,怎麼經過可達性決定回收周期呢?
有兩個原則。
-
-
- 單條引用鏈上,由最弱的一個引用類型決定
- 多條引用鏈之間,由最強的一個引用類型決定
-
上圖中對象三是一個軟可達對象,由於左半邊的鏈最弱的是弱引用,右邊的鏈最弱的是軟引用,左右兩邊最強的是軟引用。
知道了有對象須要回收以後,咱們怎麼回收呢?有四種方法
- 標記清除(Mark-Sweep)算法
先須要回收的對象作標記,以後掃描,將有標記的回收。這種算法效率不高,會產生碎片。最終致使大對象分配空間時沒法找到足夠大的內存而頻繁觸發垃圾回收。
- 複製(Copying)算法
爲了解決標記-清除算法碎片的缺點,提出了複製算法,將內存容量按照大小分紅兩份,每次使用其中的一半,當內存耗盡以後將存活對象有序的複製到另外一半中,並清理當前內存。
這種算法也有缺點:浪費通常空間;效率也跟存活對象多少有關,存活對象多算法效率會將大大下降。
- 標記整理(Mark-Compact)算法
爲了解決複製算法浪費空間的缺點,並利用標記清理的優勢,提出了這個算法,就是先標記回收對象,完了以後會將存活對象向同一端移動,而後清理須要回收的對象,這樣就保證了空間的連續性。
- 分代收集(Generational Collection)算法
從回收對象生命週期不一樣的角度講內存劃分爲不一樣的區域:新生代和老年代,並結合了前面的複製算法和標記整理算法。
新生代中存放的是:新生成的對象實例,也是垃圾回收對象最多的區域,用到的是複製算法。
老年代會存放的是:在新生代中經歷過屢次垃圾回收仍然存在的對象;另外新生代生成大對象空間不夠時也會在老年代中分配。這裏採用的是標記整理算法對老年代區域進行清理。
其中,新生代又細分爲三個區:Eden去,From Survivor區,To Survivor區,比例是8:1:1。
每次會使用Eden和一塊Survivor區(90%新生代內存)服務對象,在觸發回收機制時會採用複製算法,將兩塊區中存活的對象複製到另外10%的內存中,而後清理待回收對象。
這裏考慮一個有趣的問題,若是老年代的對象有引用到新生代的,這種狀況怎麼處理的呢?用的是寫屏障,會將這種類型的全部引用都放到一個集合中,標記時須要處理這個集合。
HotSpot JVM基於上面的垃圾回收算法實現的垃圾收集器有哪幾種呢?
- Serial GC/Serial Old GC
- 二者都屬於單線程垃圾收集器,在垃圾收集時,必須暫停全部用戶線程。
- Serial用於新生代,採用複製算法,Serial Old用於老年代,採用標記-整理算法。
- ParNew GC
- 是Serial的多線程版本,使用多線程進行垃圾收集。
- Parallel Scavenge GC/Parallel Old GC
- 都是吞吐量優先的多線程垃圾收集器
- Parallel Scavenge GC用於新生代,採用複製算法; Parallel Old GC 用於老年代,採用標記-整理算法。
- CMS GC
- Current Mark Sweep,一種以獲取最小垃圾回收停頓時間爲目標的收集器,併發,採用標記-清除算法。
- G1 GC基於CMS有很多的改進。基於標記-整理算法,能夠比較精確的控制停頓。
- 具體使用哪一種收集器,在JVM中能夠經過下面的參數指定。
參考資料:
https://blog.csdn.net/zhangerqing/article/details/8214365
https://blog.csdn.net/wxfx888/article/details/78074145