1. 程式人生 > 實用技巧 >詳解常見的垃圾收集器

詳解常見的垃圾收集器

常見的垃圾收集器分為序列、吞吐量優先和響應時間優先三種。

序列:Serial

設定引數為-XX:+UseSerialGC = Serial + SerialOld,Serial分為兩種,Serial工作在新生代,使用複製演算法;SerialOld工作在老年代,採用標記整理演算法。

  假設有4核CPU執行四個執行緒,在GC時物件的地址可能會發生變化,要發生GC時,讓所有使用者執行緒到達一個安全點停下來,垃圾回收執行緒開始回收,回收期間其他執行緒被阻塞。如果使用者執行緒不停下來,GC時物件地址變化了,就會產生問題。垃圾回收執行緒結束以後,其他執行緒再開始執行。適合堆記憶體小、個人電腦使用

吞吐量優先:Parallel Scavenge

這是1.8 預設的GC器 ,主要設定的引數為:

-XX:+UseParallelGC ~ -XX:+UseParallelOldGC 新生複製,老年標記整理

-XX:+UseAdaptiveSizePolicy 自適應調整新生代大小

-XX:GCTimeRatio=ratio 公式 1 / (1 + ratio) ratio預設值是99 ,也就是工作100分鐘內,只有1分鐘可以用於垃圾回收。如果達不到整個目標,ParallelGC會調增大堆的大小, 垃圾回收次數減少 ,以此來提高吞吐量。一般ratio設定為19。

-XX:MaxGCPauseMillis=ms 最大暫停時間也就是垃圾回收的時間,預設200ms

-XX:ParallelGCThreads=n 控制垃圾回收執行緒數量

  工作流程與序列的區別是開啟多個垃圾回收執行緒來回收,數量由電腦的CPU核數決定,同樣使用者執行緒被阻塞。特點是多執行緒收集,讓單位時間內STW時間最短 即收集次數少時間可以長點,比如一小時內2次,一次0.2秒。

響應時間優先:CMS

設定的引數為:

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld --> CMS工作在老年代,使用標記清除演算法。ParNew工作在新生地,ParNew是Serial的多執行緒版本。但CMS有時候會發生併發失敗問題,這時候老年代垃圾回收器變為SerialOld 。

-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads:併發執行緒數一般設定為並行執行緒數的1/4,比如4核就設定成1。1個執行緒來做垃圾回收,另外3個繼續執行使用者執行緒。

-XX:CMSInitiatingOccupancyFraction=percent 執行CMS回收的記憶體佔比,值越小執行垃圾回收時機越早。

-XX:+CMSScavengeBeforeRemark 在重新標記前做一些新生代的垃圾回收。因為重新標記時,新生代的物件會引用老年代的物件,標記時需要掃描整個堆,然後通過新生代引用掃描到老年代物件並作可達性分析。這種場景下新生代物件非常多並且很多會作為垃圾物件被回收,再根據新生代物件引用找到老年代物件;相當於在回收之前多做了無用的查詢,這時可以使用這個引數,使用新生代存活物件變少,減輕重新標記的壓力。

  工作流程是假設4核CPU並行執行4個執行緒。老年代記憶體不足,使用者執行緒在安全點停下來了。先進行初始標記,標記一些GCroots物件,這時候使用者執行緒被阻塞。下一步是併發標記,使用者執行緒和垃圾回收執行緒併發執行。第三步是重新標記,因為併發標記時使用者執行緒繼續執行,可能會產生新的物件,或者改變物件的一些引用。最後是併發清理,但這次清理不能回收同時產生的垃圾物件(浮動垃圾),浮動垃圾只能等到下一次垃圾回收時再清理,使用上面第三個引數調整。

  CMS使用的標記清楚演算法,自然會產生大量記憶體碎片。將來分配物件時,當新生代記憶體不夠時,老年代記憶體由於碎片過多也不足,就會造成併發失敗,這時候垃圾回收器會退化為SerialOld,垃圾回收時間會劇增。