在堆裏面存放着Java世界中幾乎全部的對象實例, 垃圾收集器在對堆進行回收前, 第一件事就是判斷哪些對象已死(可回收).html
在JDK1.2以前,使用的是引用計數器算法。 在對象中添加一個引用計數器,當有地方引用這個對象的時候,引用計數器的值就+1,當引用失效的時候,計數器的值就-1,當引用計數器被減爲零的時候,標誌着這個對象已經沒有引用了,能夠回收了!java
**問題:**若是在A類中調用B類的方法,B類中調用A類的方法,這樣當其餘全部的引用都消失了以後,A和B還有一個相互的引用,也就是說兩個對象的引用計數器各爲1,而實際上這兩個對象都已經沒有額外的引用,已是垃圾了。可是該算法並不會計算出該類型的垃圾。在主流商用語言(如Java、C#)的主流實現中, 都是經過可達性分析算法來斷定對象是否存活的: 經過一系列的稱爲 GC Roots 的對象做爲起點, 而後向下搜索; 搜索所走過的路徑稱爲引用鏈/Reference Chain, 當一個對象到 GC Roots 沒有任何引用鏈相連時, 即該對象不可達, 也就說明此對象是不可用的, 以下圖:雖然E和F相互關聯, 但它們到GC Roots是不可達的, 所以也會被斷定爲可回收的對象。 web
注: 即便在可達性分析算法中不可達的對象, VM也並非立刻對其回收, 由於要真正宣告一個對象死亡, 至少要經歷兩次標記過程: 第一次是在可達性分析後發現沒有與GC Roots相鏈接的引用鏈, 第二次是GC對在F-Queue執行隊列中的對象進行的小規模標記(對象須要覆蓋finalize()方法且沒被調用過).算法
垃圾收集策略有分代收集和分區收集。多線程
該算法分爲「標記」和「清除」兩個階段: 首先標記出全部須要回收的對象(可達性分析), 在標記完成後統一清理掉全部被標記的對象. 併發
該算法會有兩個問題:oracle
因此它通常用於"垃圾不太多的區域,好比老年代"。框架
該算法的核心是將可用內存按容量劃分爲大小相等的兩塊, 每次只用其中一塊, 當這一塊的內存用完, 就將還存活的對象(非垃圾)複製到另一塊上面, 而後把已使用過的內存空間一次清理掉.jvm
優勢:不用考慮碎片問題,方法簡單高效。 缺點:內存浪費嚴重。線程
現代商用VM的新生代均採用複製算法, 但因爲新生代中的98%的對象都是生存週期極短的, 所以並不需徹底按照1∶1的比例劃分新生代空間, 而是將新生代劃分爲一塊較大的Eden區和兩塊較小的Survivor區(HotSpot默認Eden和Survivor的大小比例爲8∶1), 每次只用Eden和其中一塊Survivor. 當發生MinorGC時, 將Eden和Survivor中還存活着的對象一次性地拷貝到另一塊Survivor上, 最後清理掉Eden和剛纔用過的Survivor的空間. 當Survivor空間不夠用(不足以保存尚存活的對象)時, 須要依賴老年代進行空間分配擔保機制, 這部份內存直接進入老年代。
複製算法的空間分配擔保: 在執行Minor GC前, VM會首先檢查老年代是否有足夠的空間存放新生代尚存活對象, 因爲新生代使用複製收集算法, 爲了提高內存利用率, 只使用了其中一個Survivor做爲輪換備份, 所以當出現大量對象在Minor GC後仍然存活的狀況時, 就須要老年代進行分配擔保, 讓Survivor沒法容納的對象直接進入老年代, 但前提是老年代須要有足夠的空間容納這些存活對象. 但存活對象的大小在實際完成GC前是沒法明確知道的, 所以Minor GC前, VM會先首先檢查老年代連續空間是否大於新生代對象總大小或歷次晉升的平均大小, 若是條件成立, 則進行Minor GC, 不然進行Full GC(讓老年代騰出更多空間). 然而取歷次晉升的對象的平均大小也是有必定風險的, 若是某次Minor GC存活後的對象突增,遠遠高於平均值的話,依然可能致使擔保失敗(Handle Promotion Failure, 老年代也沒法存放這些對象了), 此時就只好在失敗後從新發起一次Full GC(讓老年代騰出更多空間).
標記清除算法會產生內存碎片問題, 而複製算法須要有額外的內存擔保空間, 因而針對老年代的特色, 又有了標記整理算法. 標記整理算法的標記過程與標記清除算法相同, 但後續步驟再也不對可回收對象直接清理, 而是讓全部存活的對象都向一端移動,而後清理掉端邊界之外的內存.
在方法區進行垃圾回收通常」性價比」較低, 由於在方法區主要回收兩部份內容: 廢棄常量和無用的類.
回收廢棄常量與回收其餘年代中的對象相似, 但要判斷一個類是否無用則條件至關苛刻:
分區算法則將整個堆空間劃分爲連續的不一樣小區間, 每一個小區間獨立使用, 獨立回收. 這樣作的好處是能夠控制一次回收多少個小區間
在相同條件下, 堆空間越大, 一次GC耗時就越長, 從而產生的停頓也越長. 爲了更好地控制GC產生的停頓時間, 將一塊大的內存區域分割爲多個小塊, 根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減小一次GC所產生的停頓
Serial收集器是Hotspot運行在Client模式下的默認新生代收集器, 它在進行垃圾收集時,會暫停全部的工做進程,用一個線程去完成GC工做
特色:簡單高效,適合jvm管理內存不大的狀況(十兆到百兆)。
ParNew收集器實際上是Serial的多線程版本,回收策略徹底同樣,可是他們又有着不一樣。
咱們說了Parnew是多線程gc收集,因此它配合多核心的cpu效果更好,若是是一個cpu,他倆效果就差很少。(可用-XX:ParallelGCThreads參數控制GC線程數)
CMS(Concurrent Mark Sweep)收集器是一款具備劃時代意義的收集器, 一款真正意義上的併發收集器, 雖然如今已經有了理論意義上表現更好的G1收集器, 但如今主流互聯網企業線上選用的還是CMS(如Taobao),又稱多併發低暫停的收集器。
由他的英文組成能夠看出,它是基於標記-清除算法實現的。整個過程分4個步驟:
能夠看到,初始標記、從新標記須要STW(stop the world 即:掛起用戶線程)操做。由於最耗時的操做是併發標記和併發清除。因此整體上咱們認爲CMS的GC與用戶線程是併發運行的。
**優勢:**併發收集、低停頓
缺點:
-XX:CMSInitiatingOccupancyFraction
來設置GC觸發百分比(1.6後默認92%),固然咱們還得設置啓用該策略-XX:+UseCMSInitiatingOccupancyOnly
-XX:+UseCMSCompactAtFullCollection
參數,它會在GC執行完後接着進行碎片整理,可是又會有個問題,碎片整理不能併發,因此必須單線程去處理,因此若是每次GC完都整理用戶線程stop的時間累積會很長,因此XX:CMSFullGCsBeforeCompaction
參數設置隔幾回GC進行一次碎片整理(默認爲0)。同優秀的CMS垃圾回收器同樣,G1也是關注最小時延的垃圾回收器,也一樣適合大尺寸堆內存的垃圾收集,官方也推薦使用G1來代替選擇CMS。G1最大的特色是引入分區的思路,弱化分代的概念,合理利用垃圾收集各個週期的資源,解決了其餘收集器甚至CMS的衆多缺陷。
由於每一個區都有E、S、O代,因此在G1中,不須要對整個Eden等代進行回收,而是尋找可回收對象比較多的區,而後進行回收(雖然也須要STW操做,可是花費的時間是不多的),保證高效率。
G1的新生代收集跟ParNew相似,若是存活時間超過某個閾值,就會被轉移到S/O區。
年輕代內存由一組不連續的heap區組成, 這種方法使得能夠動態調整各代區域的大小
分爲如下幾個階段: