天天看點

JVM GC總結

一:Java記憶體區的簡單介紹

1.堆(Heap)

JVM初始配置設定的記憶體由-Xms指定,預設是實體記憶體的1/64。

JVM最大配置設定的記憶體由-Xmx指定,預設是實體記憶體的1/4。

預設空餘堆記憶體小于40%時,JVM就會增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=參數,來指定。

預設空餘堆記憶體小于70%時,JVM會減少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=參數,來指定。

注:伺服器一般設定-Xms和-Xmx相等以避免每次GC後頻繁的調整堆的大小。

2.分代

分代是Java垃圾收集器的一大亮點,根據對象的聲明周期長短,把堆分為3個代,Young,old和Permanent,根據不同代的特點采用不同的收集算法,揚長避短也。

Young(Nursery),年輕代。研究表明大部分對象都是朝生暮死,随生随滅的。是以所有收集器都為年輕代選擇了複制算法。複制算法的優點是隻通路活躍對象,缺點是複制成本高。因為年輕代隻有少量的對象能熬到垃圾收集器,是以隻需少量的複制成本。而且複制收集器隻通路活躍對象,對那些占了最大比率的死對象視而不見,充分發揮了它周遊空間成本低的優點。

2.1 年輕代(Young)

年輕代分為三個區,一個是Eden區,兩個Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象将被複制到Survivor區(兩個中的一個),當這個Survivor區滿時,此區的存活對象将被複制到另外一個Survivor區,當這個Survivor區也滿的時候,從第一個Survivor區複制過來的并且此時還存活的對象将被複制到年老區(Tenured)。需要注意的是,Survivor的兩個區是對稱的,沒有先後關系,是以同一個區可能同時存在從Eden複制過來的對象,和從前一個Survivor複制過來的對象,而複制到年老區的隻有從Survivor區過來的對象。而且Survivor區總有一個是空的。

2.2 Tenured(年老代)

年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命周期較長的對象。

2.3Perm(持久代)

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

二:GC的基本概念

GC分為Full GC和Minor GC,當每一塊區滿時候都會引發GC。

1.Scavenge GC

一般情況下,當新對象生成,并且在Eden申請空間失敗時,就觸發了Scavenge GC,堆Eden區域進行GC,清除非存活的對象,并且把尚且存活的對象移到Survivor區,然後整理Survivor的兩個區。

2.Full GC

對整個堆進行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,是以應該盡可能減少Full GC。有如下原因可能導緻Full GC:

2.1 Tenured被寫滿

2.2 Perm區域被寫滿

2.3 System.gc()被顯示調用

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

三:GC的相關介紹

1.垃圾收集器的介紹

1.1 串行收集器

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

1.2 并行收集器

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

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

使用并行收集器還可以調整其他一些參數,如:設定最大垃圾回收暫停即指定垃圾回收時的最長暫停時間,通過-XX:MaxGCpauseMills=<N>指定。<N>為毫秒,如果指定了此值的話,堆大小和垃圾回收相關參數會進行調整以達到指定值。設定此值可能會減少應用的吞吐量。另外還可以設定吞吐量(吞吐量為垃圾回收時間與非垃圾回收時間的比值),通過-XX:GCTimeRatio=<N>來設定,公式為1/(N+1)。例如,-XX:GCTimeRatio=19時,表示5%的時間用于垃圾回收。預設情況為99,即1%的時間用于垃圾回收。

1.3 并發收集器

可以保證大部分工作都并發進行(應用不停止),垃圾回收隻暫停很少的時間,此收集器适合對響應時間要求比較高的中、大規模應用。使用-XX:+UseConcmarkSweepGC打開。

并發收集器主要減少年老代的暫停時間,他在應用不停止的情況下使用獨立的垃圾回收線程,跟蹤可達對象。在每個年老代垃圾回收周期中,在收集初期并發收集器會對整個應用進行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次暫停稍長,在此過程中多個線程同時進行垃圾回收工作。

 并發收集器使用處理器換來短暫的停頓時間。在一個N個處理器的系統上,并發收集部分使用K/N個可用處理器進行回收,一般情況下1<=K<=N/4。

     在隻有一個處理器的主機上使用并發收集器,設定為incremental mode模式也可獲得較短的停頓時間。

 浮動垃圾:由于在應用運作的同時進行垃圾回收,是以有些垃圾可能在垃圾回收進行完成時産生,這樣就造成了"Floating Garbage",這些垃圾需要在下次垃圾回收周期時才能回收掉。是以,并發收集器一般需要20%的預留白間用于這些浮動垃圾。

Concurrent Mode Failure:并發收集器在應用運作時進行收集,是以需要保證堆在垃圾回收的這段時間有足夠的空間供程式使用,否則,垃圾回收還未完成,堆空間就先滿了。這種情況下将會發生"并發模式失敗",此時整個應用将會暫停,進行垃圾回收。

啟動并發收集器:因為并發收集在應用運作時進行收集,是以必須保證收集完成之前有足夠的記憶體空間供程式使用,否則會出現"Concurrent Mode Failure"。通過設定-XX:CMSInitiaingOccupancyFraction=<N>指定還有多少剩餘堆時進行并發收集。

收集器小結

#串行處理器

使用情況:資料量小(100MB左右),單處理器下并且對響應時間無要求的應用。

缺點:隻能用于小型應用。

#并行處理器

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

缺點:應用相應時間可能較長。

#并發處理器:傳說中的CMS

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

2.基本收集算法

2.1 引用計數法

引用計數法的實作很簡單,對于一個對象A,隻要有任何一個對象引用了A,則A的引用計數器就加1,當引用失效時,引用計數器就減1。隻要對象A的引用計數器的值為0,則對象A就不可能再被使用。然後将未被引用的對象清理即可,如下圖:

JVM GC總結

引用計數法的問題:

引用和去引用伴随加法和減法,影響性能。

很難處理循環引用。

2.2 标記清除

标記清除算法是現代垃圾回收算法的思想基礎。标記清除是伐将垃圾回收分為兩個階段:标記階段和清除階段。一種可行的實作是,在标記階段,首先通過根節點,标記所有從根節點開始的可達對象。是以,未被标記的對象就是未被引用的對象。然後,在清除階段,清除所有未被标記的對象。如下圖:

JVM GC總結

标記清除法的問題

标記完成以後需要再周遊一次整個記憶體區域,把所有沒有标記活躍的對象進行回收處理。該算法周遊整個空間的成本較大,暫停時間随空間大小線性增大而且整理後堆裡的碎片很多。

2.3 标記壓縮法

标記壓縮算法适用于存活對象較多的場合,如老年代。它在标記清除算法的基礎上做了一些優化,和标記清除算法一樣,标記壓縮算法也首先需要從跟節點開始,對所有可達對象做一次标記。但之後,它并不簡單的清理未标記的對象,而是将所有的存活對象壓縮到記憶體的一端。之後,清理邊界外所有的空間。如下圖:

JVM GC總結

2.4 複制算法

與标記清除算法相比,複制算法是一種相對高效的回收方法。不适用與存活對象較多的場合,如老年代。實作思路是:将原有的記憶體空間分為兩塊(完全相同),每次隻使用其中一塊,在垃圾回收時,将正在使用的記憶體中的存活對象複制到未使用的記憶體塊中,之後,清除正在使用的記憶體塊中的所有對象,交換兩個記憶體的角色,完成垃圾回收。如下圖:

JVM GC總結

複制算法的問題

毫無疑問複制算法最大的問題就是浪費空間。

标記整理

綜合複制算法和标記清除算法的思想,将多次回收都沒有死掉以及大對象儲存到一個擔保區,剩餘的小對象複制到一個新的區域,最後将擔保區和指派完成後的區域保留,其他清除。如下圖:

JVM GC總結

GC算法總結

引用計數法沒有被Java采用,标記壓縮相對于标記清理有優勢,因為标記清理在标記完成之後還需要再周遊一次記憶體空間清除未被标記的對象,如果記憶體空間很大的話,成本将會非常高。根據分代思想,在不同的代中,選取合适的收集算法進行優化,對于少量存活對象新生代比較适合采用複制算法,而對于大量存活對象的老年代比較适合采用标記清理或者标記壓縮算法。對于收集器而言,年輕代使用并行收集器好,年老代使用并發收集器好。

文章引用自:http://guoliangqi.iteye.com/blog/630692