前言,衆所周知,Java是由c++進化而來,c++在內存需本身申請,本身釋放,因而就有了Java的動態內存分配。書的第三章開篇,有這樣一句話描述的很妙——Java與C++之間有一堵由內存動態分配和垃圾收集技術所圍成的「高牆」,牆外的人想進去,牆內的人卻想出來。html
概述:給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;引用失效,計數器就減1;當一個對象的引用計數器爲0時,表示對象已死,可回收java
優勢:實現簡單,而且高效c++
缺點:沒法解決對象之間的循環引用問題算法
概述:解決引用計算器法的沒法判斷循環引用的問題,基本思路是,以一系列稱爲「GC Roots」的對象做爲起點,從這些節點開始向周圍搜索,經過引用鏈(Reference Chain)的方式。當對象A引用了對象B,那麼對象A和對象B之間便有了引用鏈,若是對象A是「GC Roots」,那麼對象B就被稱爲可達。當對象B引用了對象C,由於B是可達的,因此A經過B也能到達C,對象C也被稱做可達。引用鏈:A -> B -> C安全
哪些對象可做爲GC Roots的對象?數據結構
優勢:全面的分析對象是否存活多線程
缺點:實現複雜,效率低下併發
判斷對象是否存活,離不開引用。JDK1.2之前,定義引用的方法很純粹:若是reference類型的數據中存儲的數值表明的是另外一塊內存的起始地址,就稱這塊內存表明着一個引用。這就致使了引用只有兩種狀態,有或者無。兩個對象之間,存在着引用就是有,不存在引用就是沒有,沒有就被垃圾收集器給回收。可是咱們對於一些「食之無味,棄之惋惜」的對象就顯得無能爲力了。當咱們的內存足夠時,咱們並不想毀滅它,以此來避免後面的重複建立,浪費時間。因此有了如下四種定義高併發
即JDK1.8後的元空間,回收對象主要有兩部份內容:廢棄的常量和無用的類。性能
知足三個條件纔算是無用的類:
概述:算法分爲兩個階段,首先標記全部須要回收的對象,在標記完成後統一回收全部標記的對象。最基礎的收集算法。
缺點:效率不高,空間上會產生大量的空間碎片。若是程序遇到大對象須要分配時,沒法找到足夠的連續內存而不得不提早觸發另外一次的垃圾收集動做。
概述:把內存分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存快用完了,就將還存活着的對象複製到另外一塊上面,而後再把已使用過的內存空間一次性的清理掉。現代的商業機都是採用的這種收集算法,可是IBM研究代表,新生代中的對象98%都是「朝生夕死」的,並不須要按1:1來分配內存空間,而是將內存分爲一塊較大的Eden和兩塊較小的Survivor1空間,每次使用Eden和其中的一塊Survivor空間。當回收時,將Eden和Survivor中還存活的對象一次性的複製到另一塊Survivor上去,最後在清理掉Eden和剛纔使用過的Survivor空間。HotSpot虛擬機默認Eden和Survivor的大小比例是8:1。可是可能會出現複製到Survivor過程當中,存活的對象太多了,裝不下,Survivor空間不夠用。因此還須要進行分配擔保(Handle Promotion),依賴其餘內存來複制(這裏指老年代)。
優勢:簡單高效,不用考慮內存碎片等複雜狀況。
缺點:內存利用會減小一半
適用於老年代,存活率高的對象。讓全部存活的對象向一端移動,而後清理掉端邊界之外的內存。
當前商業虛擬機都是採用的「分代收集」算法,把java堆分爲新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。新生代用複製算法,由於有大量對象死去,老年代使用「標記-清理」或者「標記整理」,由於老年代存活率高。
從GC Roots出發,也就是根節點,進行可達性分析,這個過程很是消耗時間。並且可達性分析的話,須要保持「一致性」,一致性是指整個可達性分析期間的執行過程應該看起來是要停留在某個時間點上。不能夠出現分析過程當中,對象之間的引用還在發生變化。要保證可達性分析結果的正確性,準確性。因此GC會致使系統的「Stop The World」
固然因爲枚舉根節點消耗大量的時間,因此引入了OopMap這種數據結構。GC時,當執行系統停頓下來時,並不須要一個不漏的把全部執行上下文和全局的引用位置,虛擬機而是經過OopMap直接得知哪些地方存放着對象的引用
爲了配合OopMap,引入了安全點。HotSpot經過OopMap確實能夠快速且準確地完成GC Roots的枚舉,可是OopMap內容的變化的指令很是的多,若是位每一條指令都生成對應的OopMap,那將會須要大量的額外空間。因此虛擬機只在特定的時間位置,記錄了這些引用信息,並無爲每條指令都生成OopMap,這些位置便稱做安全(SafePoint),即程序執行時並不是能在全部的地方停下來,而是隻能在達到安全點時才能暫停。
如何在GC發生時,讓全部的線程都「跑」到最近的安全點去?
SafePoint很完美嗎?不見得,safePoint關注的點是正在運行的線程,那麼有些線程處於Sleep狀態或者Blocked狀態,這時候線程沒法響應中斷請求。因此引入了安全區域(Safe Region)
安全區域是指:在一段代碼中,引用關係不會發生變化。在這個區域中的任意地方開始GC都是安全的。具體描述:在線程執行到safe region中的代碼時,首先標識本身已經進入了safe region,那樣,當在這段時間裏JVM要發起GC時,就不用管標識本身爲safe region狀態的線程了。在線程要離開safe region時,它要檢查系統是否已經完成了根節點枚舉(即整個GC過程),若是完成了,那線程就繼續執行,不然他就必須等待直到收到能夠安全離開safe region的信號爲止
若是說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現。
並行(Parallel):指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待階段。
併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不必定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行在另外一個CPU上。
最基本,發展歷史最悠久的收集器。特色:在他進行垃圾收集時,必須暫停其餘全部工做線程,直到它收集結束,Stop The World。優勢:簡潔又高效。在運行在client模式下的虛擬機來講是一個很好的選擇。
是serial收集器的多線程版本。它是許多運行在Server模式下的虛擬機中首選的新生代收集器。緣由很簡單,目前只有它與serial收集器能與CMS收集器配合工做。
目標是達到一個可控的吞吐量。所謂吞吐量用於運行用戶代碼的時間與CPU總消耗時間的比值。停頓時間越短就越適合須要與用戶交互的程序,良好的響應速度能提高用戶體驗,而高吞吐量則能夠高效率地利用CPU時間,儘快完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。它也被稱爲吞吐量優先的收集器。能夠設置一個參數,讓虛擬機GC自適應的調節。即虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最適合的停頓時間或最大吞吐量。這是它與parnew收集器的一個重要區別。
是serial收集器的老年代版本,一樣是一個單線程收集器,使用標記_整理算法。
是Parallel Scavenge收集器的老年代版本,使用多線程的標記_整理算法。
是一種以獲取最短回收停頓時間爲目標的收集器。重視服務的響應速度,但願系統停頓時間最短,以給用戶帶來較好的體驗。是基於標記-清除算法實現的,它的運做過程:初始標記,併發標記,從新標記,併發清除。第一步和第三步仍然須要Stop The World。
優勢:併發收集,低停頓,又稱之爲併發低停頓收集器。
缺點:
CMS對CPU資源很是的敏感,在併發階段雖然不會致使用戶線程停頓,可是會由於佔用CPU資源而致使應用程序變慢,總吞吐量會下降。多CPU的時候,能夠用回收默認線程數(CPU數量+3)/4。使併發回收垃圾線程所佔的CPU資源使用百分比下降。CPU越多,降的多。可是CPU不多的狀況下,問題就很大,由此虛擬機提供了i-CMS增量式併發收集器,就是讓GC線程,用戶線程交替運行,儘可能減小GC線程獨佔CPU的時間,這樣會是整個垃圾回收時間增加,但對用戶程序的影響就少些。可是效果通常,不提倡使用。
CMS沒法處理浮動垃圾,因爲在併發清理階段用戶線程還在運行,可能產生新的垃圾,這一部分垃圾出如今從新標記階段以後。所以須要預留提供一部分空間給併發時的用戶線程使用,以供用戶程序正常運行,而不能像其餘收集器同樣,等到老年代幾乎被填滿了再進行收集。
是標記-清除算法的缺點,會有大量的空間碎片產生,碎片過多時,將會給大對象帶來很大的麻煩,若是沒有空間給大對象,就不得不提早觸發一次fullGC。爲了解決這個問題,能夠設置一些參數來優化,如當FullGC執行了多少次的時候,來一次空間碎片壓縮,開啓內存碎片的整理過程。可是這樣使得停頓時間變長了。
G1是一款面向服務端應用的垃圾收集器,它的使命是替換掉CMS,彌補CMS空間碎片等缺點。不一樣於其餘的分代回收算法、G1將堆空間劃分紅了互相獨立的區塊。每塊區域既有可能屬於O區、也有多是Y區,且每類區域空間能夠是不連續的(對比CMS的O區和Y區都必須是連續的)。這種將O區劃分紅多塊的理念源於:當併發後臺線程尋找可回收的對象時、有些區塊包含可回收的對象要比其餘區塊多不少。雖然在清理這些區塊時G1仍然須要暫停應用線程、但能夠用相對較少的時間優先回收包含垃圾最多區塊。這也是爲何G1命名爲Garbage First的緣由:第一時間處理垃圾最多的區塊。
平時工做中大多數系統都使用CMS、即便靜默升級到JDK7默認仍然採用CMS、那麼G1相對於CMS的區別在:
就目前而言、CMS仍是默認首選的GC策略、可能在如下場景下G1更適合:
GC模式:G1中提供了三種模式垃圾回收模式,young gc、mixed gc 和 full gc,在不一樣的條件下被觸發。
young gc:發生在年輕代的GC算法,通常對象(除了巨型對象)都是在eden region中分配內存,當全部eden region被耗盡沒法申請內存時,就會觸發一次young gc,這種觸發機制和以前的young gc差很少,執行完一次young gc,活躍對象會被拷貝到survivor region或者晉升到old region中,空閒的region會被放入空閒列表中,等待下次被使用。
參數 | 含義 |
---|---|
-XX:MaxGCPauseMillis | 設置G1收集過程目標時間,默認值200ms |
-XX:G1NewSizePercent | 新生代最小值,默認值5% |
-XX:G1MaxNewSizePercent | 新生代最大值,默認值60% |
mixed gc:當愈來愈多的對象晉升到老年代old region時,爲了不堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器,即mixed gc,該算法並非一個old gc,除了回收整個young region,還會回收一部分的old region,這裏須要注意:是一部分老年代,而不是所有老年代,能夠選擇哪些old region進行收集,從而能夠對垃圾回收的耗時時間進行控制。
那麼mixed gc何時被觸發?
先回顧一下cms的觸發機制,若是添加了如下參數:
`-XX:CMSInitiatingOccupancyFraction=``80` `-XX:+UseCMSInitiatingOccupancyOnly`
當老年代的使用率達到80%時,就會觸發一次cms gc。相對的,mixed gc中也有一個閾值參數 -XX:InitiatingHeapOccupancyPercent
,當老年代大小佔整個堆大小百分比達到該閾值時,會觸發一次mixed gc.
mixed gc的執行過程有點相似cms,主要分爲如下幾個步驟:
full gc:若是對象內存分配速度過快,mixed gc來不及回收,致使老年代被填滿,就會觸發一次full gc,G1的full gc算法就是單線程執行的serial old gc,會致使異常長時間的暫停時間,須要進行不斷的調優,儘量的避免full gc.
G1摘抄借鑑:
http://www.importnew.com/27793.html
https://juejin.im/entry/5af0832c51882567244deb44
主要是如下五種策略,目的都是在結合JVM實際狀況下,儘量的提升效率,且作到安全可靠