1. 引用計數(Reference Counting)
比較古老的回收算法。原理是此對象有一個引用,即增長一個計數,刪除一個引用則減小一個計數。垃圾回收時,只用收集計數爲0的對象。此算法最致命的是沒法處理循環引用的問題。
2. 標記-清除(Mark-Sweep)
此算法執行分兩階段。第一階段從引用根節點開始標記全部被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。此算法須要暫停整個應用,同時,會產生內存碎片。
3. 複製(Copying)
此算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中。算法每次只處理正在使用中的對象,所以複製成本比較小,同時複製過去之後還能進行相應的內存整理,不過出現「碎片」問題。固然,此算法的缺點也是很明顯的,就是須要兩倍內存空間。
4. 標記-整理(Mark-Compact)
此算法結合了 「標記-清除」和「複製」兩個算法的優勢。也是分兩階段,第一階段從根節點開始標記全部被引用對象,第二階段遍歷整個堆,把清除未標記對象而且把存活對象 「壓縮」到堆的其中一塊,按順序排放。此算法避免了「標記-清除」的碎片問題,同時也避免了「複製」算法的空間問題。
5. 增量收集(Incremental Collecting)
實施垃圾回收算法,即:在應用進行的同時進行垃圾回收。不知道什麼緣由JDK5.0中的收集器沒有使用這種算法的。
6. 分代(Generational Collecting)算法
基於對對象生命週期分析後得出的垃圾回收算法。把對象分爲年青代、年老代、持久代,對不一樣生命週期的對象使用不一樣的算法(上述方式中的一個)進行回收。如今的垃圾回收器(從J2SE1.2開始)都是使用此算法的。服務器
a. Young(年輕代)
年輕代分三個區。一個Eden區,兩個 Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到Survivor區(兩個中的一個),當這個 Survivor區滿時,此區的存活對象將被複制到另一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複製過來的而且此時還存活的對象,將被複制「年老區(Tenured)」。須要注意,Survivor的兩個區是對稱的,沒前後關係,因此同一個區中可能同時存在從Eden複製過來對象,和從前一個Survivor複製過來的對象,而複製到年老區的只有從第一個Survivor去過來的對象。並且,Survivor區總有一個是空的。
b. Tenured(年老代)
年老代存放從年輕代存活的對象。通常來講年老代存放的都是生命期較長的對象。
c. Perm(持久代)
用於存放靜態文件,現在Java類、方法等。持久代對垃圾回收沒有顯著影響,可是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候須要設置一個比較大的持久代空間來存放這些運行過程當中新增的類。持久代大小經過-XX:MaxPermSize=<N>進行設置。多線程
1. Scavenge GC
通常狀況下,當新對象生成,而且在Eden申請空間失敗時,就好觸發Scavenge GC,堆Eden區域進行GC,清除非存活對象,而且把尚且存活的對象移動到Survivor區。而後整理Survivor的兩個區。
2. Full GC
對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,所以應該儘量減小Full GC。有以下緣由可能致使Full GC:
* Tenured被寫滿
* Perm域被寫滿
* System.gc()被顯示調用
* 上一次GC以後Heap的各域分配策略動態變化併發
1. 串行收集器
使用單線程處理全部垃圾回收工做,由於無需多線程交互,因此效率比較高。可是,也沒法使用多處理器的優點,因此此收集器適合單處理器機器。固然,此收集器也能夠用在小數據量(100M左右)狀況下的多處理器機器上。可使用-XX:+UseSerialGC打開。
2. 並行收集器
1. 對年輕代進行並行垃圾回收,所以能夠減小垃圾回收時間。通常在多線程多處理器機器上使用。使用-XX:+UseParallelGC.打開。並行收集器在J2SE5.0更新上引入,在Java SE6.0中進行了加強--能夠堆年老代進行並行收集。若是年老代不使用併發收集的話,是使用單線程進行垃圾回收,所以會制約擴展能力。使用-XX:+UseParallelOldGC打開。
2. 使用-XX:ParallelGCThreads=<N>設置並行垃圾回收的線程數。此值能夠設置與機器處理器數量相等。
3. 此收集器能夠進行以下配置:
* 最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,經過-XX:MaxGCPauseMillis=<N>指定。<N>爲毫秒.若是指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減小應用的吞吐量。
* 吞吐量:吞吐量爲垃圾回收時間與非垃圾回收時間的比值,經過-XX:GCTimeRatio=<N>來設定,公式爲1/(1+N)。例如,-XX:GCTimeRatio=19時,表示5%的時間用於垃圾回收。默認狀況爲99,即1%的時間用於垃圾回收。優化
3. 併發收集器
能夠保證大部分工做都併發進行(應用不中止),垃圾回收只暫停不多的時間,此收集器適合對響應時間要求比較高的中、大規模應用。使用-XX:+UseConcMarkSweepGC打開。
1. 併發收集器主要減小年老代的暫停時間,他在應用不中止的狀況下使用獨立的垃圾回收線程,跟蹤可達對象。在每一個年老代垃圾回收週期中,在收集初期併發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程當中多個線程同時進行垃圾回收工做。
2. 併發收集器使用處理器換來短暫的停頓時間。在一個N個處理器的系統上,併發收集部分使用K/N個可用處理器進行回收,通常狀況下1<=K<=N/4。
3. 在只有一個處理器的主機上使用併發收集器,設置爲incremental mode模式也可得到較短的停頓時間。
4. 浮動垃圾:因爲在應用運行的同時進行垃圾回收,因此有些垃圾可能在垃圾回收進行完成時產生,這樣就形成了「Floating Garbage」,這些垃圾須要在下次垃圾回收週期時才能回收掉。因此,併發收集器通常須要20%的預留空間用於這些浮動垃圾。
5. Concurrent Mode Failure:併發收集器在應用運行時進行收集,因此須要保證堆在垃圾回收的這段時間有足夠的空間供程序使用,不然,垃圾回收還未完成,堆空間先滿了。這種狀況下將會發生「併發模式失敗」,此時整個應用將會暫停,進行垃圾回收。
6. 啓動併發收集器:由於併發收集在應用運行時進行收集,因此必須保證收集完成以前有足夠的內存空間供程序使用,不然會出現「Concurrent Mode Failure」。經過設置-XX:CMSInitiatingOccupancyFraction=<N>指定還有多少剩餘堆時開始執行併發收集spa
4. 小結
* 串行處理器:
--適用狀況:數據量比較小(100M左右);單處理器下而且對響應時間無要求的應用。
--缺點:只能用於小型應用
* 並行處理器:
--適用狀況:「對吞吐量有高要求」,多CPU、對應用響應時間無要求的中、大型應用。舉例:後臺處理、科學計算。
--缺點:應用響應時間可能較長
* 併發處理器:
--適用狀況:「對響應時間有高要求」,多CPU、對應用響應時間有較高要求的中、大型應用。舉例:Web服務器/應用服務器、電信交換、集成開發環境。線程
1. 年輕代大小選擇
* 響應時間優先的應用:儘量設大,直到接近系統的最低響應時間限制(根據實際狀況選擇)。在此種狀況下,年輕代收集發生的頻率也是最小的。同時,減小到達年老代的對象。
* 吞吐量優先的應用:儘量的設置大,可能到達Gbit的程度。由於對響應時間沒有要求,垃圾收集能夠並行進行,通常適合8CPU以上的應用。對象
2. 年老代大小選擇
* 響應時間優先的應用:年老代使用併發收集器,因此其大小須要當心設置,通常要考慮併發會話率和會話持續時間等一些參數。若是堆設置小了,能夠會形成內存碎片、高回收頻率以及應用暫停而使用傳統的標記清除方式;若是堆大了,則須要較長的收集時間。最優化的方案,通常須要參考如下數據得到:
o 併發垃圾收集信息
o 持久代併發收集次數
o 傳統GC信息
o 花在年輕代和年老代回收上的時間比例
減小年輕代和年老代花費的時間,通常會提升應用的效率
* 吞吐量優先的應用:通常吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。緣由是,這樣能夠儘量回收掉大部分短時間對象,減小中期的對象,而年老代盡存放長期存活對象。blog
3. 較小堆引發的碎片問題
由於年老代的併發收集器使用標記、清除算法,因此不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合併,這樣能夠分配給較大的對象。可是,當堆空間較小時,運行一段時間之後,就會出現「碎片」,若是併發收集器找不到足夠的空間,那麼併發收集器將會中止,而後使用傳統的標記、清除方式進行回收。若是出現「碎片」,可能須要進行以下配置:
* -XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啓對年老代的壓縮。
* -XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的狀況下,這裏設置多少次Full GC後,對年老代進行壓縮生命週期
參考:http://chenchendefeng.iteye.com/blog/455883