天天看點

Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器

一、為什麼要分代?

分代的垃圾回收政策,是基于這樣一個事實:不同的對象的生命周期是不一樣的。是以,不同生命周期的對象可以采取不同的收集方式,以便提高回收效率。

在Java程式運作的過程中,會産生大量的對象,其中有些對象是與業務資訊相關,比如Http請求中的Session對象、線程、Socket連接配接,這類對象跟業務直接挂鈎,是以生命周期比較長。但是還有一些對象,主要是程式運作過程中生成的臨時變量,這些對象生命周期會比較短,比如:String對象,由于其不變類的特性,系統會産生大量的這些對象,有些對象甚至隻用一次即可回收。

試想,在不進行對象存活時間區分的情況下,每次垃圾回收都是對整個堆空間進行回收,花費時間相對會長,同時,因為每次回收都需要周遊所有存活對象,但實際上,對于生命周期長的對象而言,這種周遊是沒有效果的,因為可能進行了很多次周遊,但是他們依舊存在。是以,分代垃圾回收采用分治的思想,進行代的劃分,把不同生命周期的對象放在不同代上,不同代上采用最适合它的垃圾回收方式進行回收。

二、如何分代?

如圖所示,虛拟機中的共劃分為三個代:年輕代(Young Generation)、年老點(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類資訊,與垃圾收集要收集的Java對象關系不大。年輕代和年老代的劃分是對垃圾收集影響比較大的。

年輕代

所有新生成的對象首先都是放在年輕代的。年輕代的目标就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區。一個Eden區,兩個Survivor區(一般而言)。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象将被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象将被複制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區複制過來的并且此時還存活的對象,将被複制“年老區(Tenured)”。需要注意,Survivor的兩個區是對稱的,沒先後關系,是以同一個區中可能同時存在從Eden複制過來 對象,和從前一個Survivor複制過來的對象,而複制到年老區的隻有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。同時,根據程式需要,Survivor區是可以配置為多個的(多于兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。

年老代

在年輕代中經曆了N次垃圾回收後仍然存活的對象,就會被放到年老代中。是以,可以認為年老代中存放的都是一些生命周期較長的對象。

持久代

用于存放靜态檔案,如今Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動态生成或者調用一些class,例如Hibernate等,在這種時候需要設定一個比較大的持久代空間來存放這些運作過程中新增的類。持久代大小通過-XX:MaxPermSize=進行設定。

三、什麼情況下觸發垃圾回收?

由于對象進行了分代處理,是以垃圾回收區域、時間也不一樣。GC有兩種類型:Scavenge GC和Full GC。

Scavenge GC

一般情況下,當新對象生成,并且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,并且把尚且存活的對象移動到Survivor區。然後整理Survivor的兩個區。這種方式的GC是對年輕代的Eden區進行,不會影響到年老代。因為大部分對象都是從Eden區開始的,同時Eden區不會配置設定的很大,是以Eden區的GC會頻繁進行。因而,一般在這裡需要使用速度快、效率高的算法,使Eden去能盡快空閑出來。

Full GC

對整個堆進行整理,包括Young、Tenured和Perm。Full GC因為需要對整個對進行回收,是以比Scavenge GC要慢,是以應該盡可能減少Full GC的次數。在對JVM調優的過程中,很大一部分工作就是對于FullGC的調節。有如下原因可能導緻Full GC。

· 年老代(Tenured)被寫滿

· 持久代(Perm)被寫滿

· System.gc()被顯示調用

·上一次GC之後Heap的各域配置設定政策動态變化

四、分代垃圾回收流程示意圖

Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器
Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器
Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器
Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器

五、選擇合适的垃圾收集算法

串行收集器

Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器

用單線程處理所有垃圾回收工作,因為無需多線程互動,是以效率比較高。但是,也無法使用多處理器的優勢,是以此收集器适合單處理器機器。當然,此收集器也可以用在小資料量(100M左右)情況下的多處理器機器上。可以使用-XX:+UseSerialGC打開。

并行收集器

Java垃圾回收圖解_JVM調優總結(四):圖解分代垃圾回收器

對年輕代進行并行垃圾回收,是以可以減少垃圾回收時間。一般在多線程多處理器機器上使用。使用-XX:+UseParallelGC.打開。并行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中進行了增強--可以對年老代進行并行收集。如果年老代不使用并發收集的話,預設是使用單線程進行垃圾回收,是以會制約擴充能力。使用-XX:+UseParallelOldGC打開。

使用-XX:ParallelGCThreads=設定并行垃圾回收的線程數。此值可以設定與機器處理器數量相等。

此收集器可以進行如下配置:

最大垃圾回收暫停:指定垃圾回收時的最長暫停時間,通過-XX:MaxGCPauseMillis=指定。為毫秒.如果指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減少應用的吞吐量。

吞吐量:吞吐量為垃圾回收時間與非垃圾回收時間的比值,通過-XX:GCTimeRatio=來設定,公式為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=指定還有多少剩餘堆時開始執行并發收集

六、小結

串行處理器

适用情況:資料量比較小(100M左右);單處理器下并且對響應時間無要求的應用。

缺點:隻能用于小型應用

并行處理器

适用情況:“對吞吐量有高要求”,多CPU、對應用響應時間無要求的中、大型應用。舉例:背景處理、科學計算。

缺點:垃圾收集過程中應用響應時間可能加長

并發處理器

适用情況:“對響應時間有高要求”,多CPU、對應用響應時間有較高要求的中、大型應用。舉例:Web伺服器/應用伺服器、電信交換、內建開發環境。

來源:https://pengjiaheng.iteye.com

---END---

近期熱文:

————--^^^--————

看更多好文