本文系轉載,原文地址:http://blog.csdn.net/willfcareer/archive/2010/08/23/5833082.aspx
Java虛擬機器垃圾收集器分析
1 、使用中的物件
l 追蹤 root 對象演算法: 深度追蹤 root 對象,將 heap 中所有被引用到的 root 做標誌,所有未被標誌的對象視為非使用中的物件,所佔用的空間視為非活動記憶體。
2 、常用演算法
l Copy 演算法
1 方法:將記憶體分為兩個地區( from space 和 to space )。所有的對象分配記憶體都分配到 from space 。在清理非使用中的物件階段,把所有標誌為活動的對象, copy 到 to
space ,之後清楚 from space 空間。然後互換 from sapce 和 to space 的身份。既原先的 from space 變成 to
sapce ,原先的 to space 變成 from space 。每次清理,重複上述過程。
2 優點: copy 演算法不理會非使用中的物件, copy 數量僅僅取決為使用中的物件的數量。並且在 copy 的同時,整理了 heap 空間,即, to
space 的空間使用始終是連續的,記憶體使用量效率得到提高。
3 缺點:劃分 from space 和 to space ,記憶體的使用率是 1 / 2 。一般在 Young
Generation 中使用。
l Compaction 演算法
1 方法:在清理非使用中的物件階段,刪除非使用中的物件佔用記憶體,並且把使用中的物件向 heap 的底部移動,直到所有的使用中的物件被移到 heap 的一側。
2 優點:無須劃分 from sapce 和 to space ,提高記憶體的使用率。並且 compaction 後的記憶體空間也是連續分配的。
3 缺點:該演算法相對比較複雜。一般在 Tenured Generation 中使用
1、 記憶體劃分
l Young Generation
1 生命週期很短的對象,歸為 young generation 。由於生命週期很短,這部分對象在 gc 的時候,很大部分的對象已經成為非使用中的物件。因此針對 young
generation 的對象,採用 copy 演算法,只需要將少量的存活下來的對象 copy 到 to space 。存活的對象數量越少,那麼 copy 演算法的效率越高。
2 young generation 的 gc 稱為 minor gc 。經過數次 minor
gc ,依舊存活的對象,將被移出 young generation ,移到 tenured generation
3 young generation 分為:
1 eden :每當對象建立的時候,總是被分配在這個地區
2 survivor1 : copy 演算法中的 from space
3 survivor2 : copy 演算法中的 to sapce (備忘:其中 survivor1 和 survivor2 的身份在每次 minor
gc 後被互換)
4 minor gc 的時候,會把 eden+survivor1(2) 的對象 copy 到 survivor2(1) 去。
l Tenured Generation
1 生命週期較長的對象,歸入到 tenured generation 。一般是經過多次 minor gc ,還 依舊存活的對象,將移入到 tenured
generation 。(當然,在 minor gc 中如果存活的對象的超過 survivor 的容量,放不下的對象會直接移入到 tenured generation )
2 tenured generation 的 gc 稱為 major gc ,就是通常說的 full
gc 。
3 採用 compactiion 演算法。由於 tenured generaion 地區比較大,而且通常對象生命週期都比較常, compaction 需要一定時間。所以這部分的 gc 時間比較長。
4 minor gc 可能引發 full gc 。當 eden + from space 的空間大於 tenured
generation 區的剩餘空間時,會引發 full gc 。這是悲觀演算法,要確保 eden + from space 的對象如果都存活,必須有足夠的 tenured generation 空間存放這些對象。
l Permanet Generation
1 該地區比較穩定,主要用於存放 classloader 資訊,比如類資訊和 method 資訊。
2 對於 spring hibernate 這些需要動態類型支援的架構,這個地區需要足夠的空間。
演算法補充:
基本回收演算法
- 引用計數(Reference Counting)
比較古老的回收演算法。原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。記憶體回收時,只用收集計數為0的對象。此演算法最致命的是無法處理循環參考的問題。
- 標記-清除(Mark-Sweep)
此演算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍曆整個堆,把未標記的對象清除。此演算法需要暫停整個應用,同時,會產生記憶體片段。
- 複製(Copying)
此演算法把記憶體空間劃為兩個相等的地區,每次只使用其中一個地區。記憶體回收時,遍曆當前使用地區,把正在使用中的對象複製到另外 一個地區中。次演算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的記憶體整理,不過出現“片段”問題。當然,此演算法的缺 點也是很明顯的,就是需要兩倍記憶體空間。
- 標記-整理(Mark-Compact)
此演算法結合了“標記-清除”和“複製”兩個演算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍曆 整個堆,把清除未標記對象並且把存活對象“壓縮”到堆的其中一塊,按順序排放。此演算法避免了“標記-清除”的片段問題,同時也避免了“複製”演算法的空間問 題。
- 增量收集(Incremental Collecting)
實施記憶體回收演算法,即:在應用進行的同時進行記憶體回收。不知道什麼原因JDK5.0中的收集器沒有使用這種演算法的。
- 分代(Generational Collecting)
基於對對象生命週期分析後得出的記憶體回收演算法。把對象分為年青代、年老代、持久代,對不同生命週期的對象使用不同的演算法(上述方式中的一個)進行回收。現在的記憶體回收行程(從J2SE1.2開始)都是使用此演算法的。
二、記憶體回收行程
目前的收集器主要有三種:串列收集器、並行收集器、並發收集器 。
- 串列收集器
使用單線程處理所有記憶體回收工作,因為無需多線程互動,所以效率比較高。但是,也無法使用多處理器的優勢,所以此收集器適合單一處理器機器。當然,此收集器也可以用在小資料量(100M 左右)情況下的多處理器機器上。可以使用-XX:+UseSerialGC 開啟。
- 並行收集器
- 對年輕代進行並行記憶體回收,因此可以減少記憶體回收時間。一般在多線程多處理器機器上使用。使用-XX:+UseParallelGC .開啟。並行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中進行了增強--可以堆年老代進行並行收集。如果年老代不使用並發收集的話,是使用單線程進行記憶體回收 ,因此會制約擴充能力。使用-XX:+UseParallelOldGC 開啟。
- 使用-XX:ParallelGCThreads=<N> 設定並行記憶體回收的線程數。此值可以設定與機器處理器數量相等 。
- 此收集器可以進行如下配置:
- 最大記憶體回收暫停: 指定記憶體回收時的最長暫停時間,通過-XX:MaxGCPauseMillis=<N> 指定。<N>為毫秒.如果指定了此值的話,堆大小和記憶體回收相關參數會進行調整以達到指定值 。設定此值可能會減少應用的輸送量。
- 輸送量: 輸送量為記憶體回收時間與非記憶體回收時間的比值 ,通過-XX:GCTimeRatio=<N> 來設定,公式為1/(1+N) 。例如,-XX:GCTimeRatio=19時,表示5%的時間用於記憶體回收。預設情況為99,即1%的時間用於記憶體回收。
- 並發收集器
可以保證大部分工作都並發進行(應用不停止),記憶體回收只暫停很少的時間,此收集器適合對回應時間要求比較高的中、大規模應用。使用-XX:+UseConcMarkSweepGC 開啟。
- 並發收集器主要減少年老代的暫停時間,他在應用不停止的情況下使用獨立的記憶體回收線程,跟蹤可達對象。在每個年老代記憶體回收周期 中,在收集初期並發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程中多個線程同時進行記憶體回收工作。
- 並發收集器使用處理器換來短暫的停頓時間 。在一個N個處理器的系統上,並發收集部分使用K/N 個可用處理器進行回收,一般情況下1<=K<=N/4 。
- 在只有一個處理器的主機上使用並發收集器 ,設定為incremental mode 模式也可獲得較短的停頓時間。
- 浮動垃圾 :由於在應用啟動並執行同時進行記憶體回收,所以有些垃圾可能在記憶體回收進行完成時產生,這樣就造成了“Floating Garbage”,這些垃圾需要在下次記憶體回收周期時才能回收掉。所以,並發收集器一般需要20% 的預留空間用於這些浮動垃圾。
- Concurrent Mode Failure :並發收集器在應用運行時進行收集,所以需要保證堆在記憶體回收的這段時間有足夠的空間供程式使用,否則,記憶體回收還未完成,堆空間先滿了。這種情況下將會發生“併發模式失敗”,此時整個應用將會暫停,進行記憶體回收。
- 啟動並發收集器 :因為並發收集在應用運行時進行收集,所以必須保證收集完成之前有足夠的記憶體空間供程式使用,否則會出現“Concurrent Mode Failure”。通過設定-XX:CMSInitiatingOccupancyFraction=<N> 指定還有多少剩餘堆時開始執行並發收集
- 小結
- 串列處理器:
--適用情況:資料量比較小(100M左右);單一處理器下並且對回應時間無要求的應用。
--缺點:只能用於小型應用
- 平行處理器:
--適用情況:“對輸送量有高要求”,多CPU、對應用回應時間無要求的中、大型應用。舉例:幕後處理、科學計算。
--缺點:應用回應時間可能較長
- 並發處理器:
--適用情況:“對回應時間有高要求”,多CPU、對應用回應時間有較高要求的中、大型應用。舉例:Web伺服器/應用伺服器、電信交換、整合式開發環境。