GC算法基礎

尋找垃圾對象的算法:1. 引用計數(沒法處理循環引用) 2. 根尋法(被普遍引用在gc算法中)算法

清理垃圾的算法: 1. 標記複製  2. 標記清理  3. 標記整理數據結構

分代算法的好處:併發

   1. 分代處理,能夠減小一次處理的內存大小,減小停頓時間。  jvm

   2. 不一樣的代有不一樣的特色,再加上有針對性的gc算法和代碼優化,總體的性能更好:性能

        年輕代特色是分配快,對象存活時間短,因此採用標記複製時間消耗不會過高,效率高。優化手段就是減小分配需求的速度,減小對象的存活時間,這樣使得youngGc頻率和停頓都下降,也減小了老年代的壓力,是頗有效的優化手段。優化

        老年代的特色是內存較大,對象存活時間久,因此相對來講比較年輕代要複雜。因此有各類各樣不一樣的gc算法來對老年代進行處理,考量點主要是吞吐量和停頓時間。spa

       老年代擔任的一個很重要的角色是給youngGC作擔保,保證對象的晉升能夠成功。因此這裏的一個重要的指標是,FullGC發生的頻率。FullGC的發生,通常是因爲老年代的內存清理速度更不上晉升的速度,或者是老年代的內存碎片,因此保證對象的存活時間儘量的短是有不少好處的。線程

 

GC算法: 日誌

1. 串行GC對象

2. ThroughPutGC(並行GC)—目的:提升吞吐量

3. ConcurrentGC— 目的:下降停頓

 

垃圾收集器:

Serial收集器:

   年輕代的收集器+串行GC+標記複製

ParNew收集器:

   年輕代的收集器+並行GC+標記複製

Parallel Scavenge收集器:

   年輕代的收集器+並行GC+標記複製。 和ParNew的區別多是引進了用戶設定目標,gc本身調整大小的機制

Serial Old收集器:

   老年代的收集器+串行GC+標記整理

Parallel Old收集器:

   老年代的收集器+並行GC+標記整理

CMS收集器:

   老年代的收集器+併發GC+標記清理

G1收集器:

   年輕代+老年代的收集器

   年輕代=並行GC+標記複製

   老年代=併發GC+標記整理

 

GC算法選擇:

  Gc算法的選擇考慮的方向主要是停頓時間和吞吐量倆個方面。須要根據程序的特徵,CPU資源來進行權衡。

  1. 先從吞吐量來講,其實考慮的主要是可用的cpu資源有多少(即有多少空閒的cpu資源)。若是空閒的cpu資源較少,使用throughput類的gc會更好,由於它自己的收集效率就比並發的gc好。而併發的gc算法會和應用程序搶奪cpu資源。就會形成吞吐量降低。而若是空閒的cpu資源不少,那麼併發的gc能夠利用這些空閒的cpu來併發的完成gc,並且對應用程序的影響是很小的。反正那些cpu空着也是空着。若是這時使用throughput收集器,這些空閒的cpu會被白白浪費,並且還會stw來獨佔cpu進行gc,若是gc自己沒法徹底利用這些cpu資源,並且還會影響應用程序,就得不償失了。

  2. 在衡量停頓時間上,通常來講,throughput的收集器的90%的響應時間都要比並發gc的快,由於90%的時間是沒有gc的,只有在發生full-gc時纔會致使響應時間過長。 可是,當full-gc發生的更頻繁的時候,這個百分比也就會降低。固然,當發生這種狀況是,併發gc也許也會難以免full-gc。

 

 

CMS和G1的選擇:

   通常認爲,當堆比較大時(大於4G),使用G1比較好,不然CMS的性能會更好一點。緣由在於,gc消耗的時間和堆的大小是有很大的關係的,好比標記須要掃描整個堆。可是G1對堆進行了進一步的劃分,在清理週期中,能夠只清理垃圾最多的一部分分區,因此在大堆的狀況下,性能是比CMS要好一些。另外,因爲G1是標記複製的算法,因此不容易形成內存碎片。 

   反過來,因爲G1的算法和數據結構要更加複雜,因此它的內存和cpu的消耗也就更大。若是堆自己就比較小的話,使用G1會形成應用程序可使用的堆大小會更小。

    

 

GC調優基礎:

1. 調整堆的大小:

     A.堆太小,致使gc頻率變高。 堆太大,致使單次gc的時間變長。因此,要經過調整對的大小來找到一個咱們認爲相對合適的目標。目標主要從停頓時間和吞吐量倆個方面來考慮。

     B.堆的大小,不要超過機器的物理內存,由於jvm對底層的swap並沒有感知,swap帶來的gc停頓變長,可能會致使併發失效,從而致使full-gc

     C.-Xms4096m -Xmx4096m 這種最大和初始同樣時,堆的大小就不會由於自適應調整而改變。當咱們知道最合適的堆的大小時,這種設置是有好處的,由於jvm不須要再進行堆的大小計算和調整。可是當咱們不知道合適的堆的大小應該是多少時,我以爲仍是不要這麼搞吧。

 

2. 調整代的大小:

    新生代過小,會致使youngGc頻率高,對象晉升快,對老年代的壓力增大。 新生代太大,新生代的gc時間可能會變長,對象晉升變慢,老年代的大小受限,可能會致使full-gc。

    老年代過小,可能會致使full-gc變多。老年代太大,可能會致使老年代的gc太慢。

    -XX:NewRatio=

    -XX:NewSize=

     -XmnN
    -XX:MaxNewSize=N

3. 調整永久代和元空間的大小:

     永久代和元空間也會觸發fullgc,因此也須要關注一下大小是否合適。

 

4. 控制併發:

    gc的線程任務是計算密集型

    -XX:ParallelGCThreads=N 來控制並並GC的線程數,好比:

    a.使用-XX:+UseParallelGC收集新生代空間
    b.使用-XX:+UseParallelOldGC收集老年代空間

    c. CMS的「STW」階段(不包括Full-GC)

    d. G1的「STW」階段(不包括Full-GC)

    e.使用-XX:+UseParNewGC收集的新生代空間  

    f.使用-XX:+UseG1GC收集的新生代空間

5. 自適應調整:

    用戶設定目標:響應時間和gc消耗時間佔比 -XX:MaxGCPauseMillis=N 和 -XX:GCTimeRatio=N ,這倆個配置會同時影響年輕代和老年代

    自適應調整會調整堆的大小,新生代和老年代的大小來儘可能適用目標值。若是堆的初始值和最大值同樣,則自適應調整不會對堆的總體大小進行調整。若是堆,年輕代的初始值和最大值同樣,則自適應就徹底失效了,不過survivor仍是會進行自適應調整。

   -XX:-UseAdaptiveSizePolicy  關閉自適應(默認是開)。 -XX:+PrintAdaptiveSizePolicy 在gc時會打印調整的信息。

   對精細化計算過的jvm內存比例,能夠把各個區域的大小進行限定,這樣能夠減小堆自適應的消耗。  

 

 

Throughput收集器:

   Gc分爲youngGc和fullGC。正常的FullGc不會對永久區進行回收,當回收區滿時,會觸發fullGc,這時會對永久區進行回收。

   調優:

     1.自適應調整:首先會調整堆的大小來達到目標停頓時間,而後慢慢增大堆的大小來儘量達到目標吞吐量。而後又開始下降堆的大小來減小jvm的內存佔用。目標值的設定必定要合理,太極端會致使極端的問題。

     2.調整並行線程數,由於是stw,並且gc的任務是計算密集型的,因此線程數根據cpu和計算密集型的公式就好。

 

CMS:

    CMS有三個gc動做:1. yooung區的gc(STW)  2.老年代的gc(併發gc+標記清除)  3.full-gc(串行full-gc)

    老年代的GC過程:  

  1. 初始標記(STW): 標記那些直接被Root對象引用的對象

                 2.併發標記: 併發的進行Root Tracing的過程,時間較長。沒法處理浮動垃圾。

                 3.從新標記(STW): 主要是對步驟2中可能出現的遺漏進行補充,時間比1長,可是遠比2短

                 4.併發清理:併發的進行垃圾的清理,不會進行內存壓縮,因此可能會形成內存碎片

    一 YGC   89.853: [GC 89.853: [ParNew: 629120K->69888K(629120K), 0.1218970 secs]

  •                             1303940K->772142K(2027264K), 0.1220090 secs]
  •                         [Times: user=0.42 sys=0.02, real=0.12 secs]
  •                       user的時間表示cpu時間,real表示時鐘的時間。對於併發gc,real時間通常比user時間少。
  •   二初始標記  89.976: [GC [1 CMS-initial-mark: 702254K(1398144K)]
  •                             772530K(2027264K), 0.0830120 secs]
  •                             [Times: user=0.08 sys=0.00, real=0.08 secs]

   三併發標記   90.059: [CMS-concurrent-mark-start]

                            90.887: [CMS-concurrent-mark: 0.823/0.828 secs]

                             [Times: user=1.11 sys=0.00, real=0.83 secs]

   四預清理       90.887: [CMS-concurrent-preclean-start]

                             90.892: [CMS-concurrent-preclean: 0.005/0.005 secs]

                             [Times: user=0.01 sys=0.00, real=0.01 secs]

   五從新標記    90.892: [CMS-concurrent-abortable-preclean-start]

                             92.392: [GC 92.393: [ParNew: 629120K->69888K(629120K), 0.1289040 secs]

                                          1331374K->803967K(2027264K), 0.1290200 secs]

                                          [Times: user=0.44 sys=0.01, real=0.12 secs]

                             94.473: [CMS-concurrent-abortable-preclean: 3.451/3.581 secs]

                                           [Times: user=5.03 sys=0.03, real=3.58 secs]

                            94.474: [GC[YG occupancy: 466937 K (629120 K)]

                                          94.474: [Rescan (parallel) , 0.1850000 secs]

                                          94.659: [weak refs processing, 0.0000370 secs]

                                          94.659: [scrub string table, 0.0011530 secs]

                                                       [1 CMS-remark: 734079K(1398144K)]

                                                       1201017K(2027264K), 0.1863430 secs]

                                         [Times: user=0.60 sys=0.01, real=0.18 secs]

                           這個過程執行了多個步驟,首先是可中斷的預清理。因爲jvm爲了不倆次連續的停頓(剛發生一次YGC後,立馬發生從新標記),因此在發生YGC後,jvm預測下一個YGC發生的時間週期,在這個週期中間中止了可停頓預清理,而後開始了從新標記。

              這也意味了,老年代的併發週期和YGC是併發進行的。

     六併發清理階段   94.661: [CMS-concurrent-sweep-start]

                                    95.223: [GC 95.223: [ParNew: 629120K->69888K(629120K), 0.1322530 secs]

                                                                       999428K->472094K(2027264K), 0.1323690 secs]

                                                                        [Times: user=0.43 sys=0.00, real=0.13 secs]

                                    95.474: [CMS-concurrent-sweep: 0.680/0.813 secs]

                                                  [Times: user=1.45 sys=0.00, real=0.82 secs]

                              這裏日誌表示,在併發清理階段,發送了YGC,也代表老年代的併發週期和YGC是併發進行的。並且在併發週期中至少會發生一次YGC,就是可中斷預清理的過程當中。

      七併發重置階段   95.474: [CMS-concurrent-reset-start]

                                     95.479: [CMS-concurrent-reset: 0.005/0.005 secs]

                                                   [Times: user=0.00 sys=0.00, real=0.00 secs]

                               這是併發週期的最後一個階段,可是咱們無法中日誌中得知這個階段回收了多少內存,只能從上下的GC日誌中進行推測。

      八併發模式失敗(concurrent mode failure)

                                     267.006: [GC 267.006: [ParNew: 629120K->629120K(629120K), 0.0000200 secs]

                                                    267.006: [CMS267.350: [CMS-concurrent-mark: 2.683/2.804 secs]

                                                    [Times: user=4.81 sys=0.02, real=2.80 secs]

                                                   (concurrent mode failure):

                                                  1378132K->1366755K(1398144K), 5.6213320 secs]

                                                    2007252K->1366755K(2027264K),

                                                 [CMS Perm : 57231K->57222K(95548K)], 5.6215150 secs]

                                                  [Times: user=5.63 sys=0.00, real=5.62 secs]

                                這是在發生YGC時,老年代沒有足夠的空間來容納晉升的對象,就退化成了FULL-GC。這個過程是串行的,因此很慢。

         九晉升失敗       6043.903: [GC 6043.903:

             [ParNew (promotion failed): 614254K->629120K(629120K), 0.1619839 secs]

             6044.217: [CMS: 1342523K->1336533K(2027264K), 30.7884210 secs]

             2004251K->1336533K(1398144K),

             [CMS Perm : 57231K->57231K(95548K)], 28.1361340 secs]

             [Times: user=28.13 sys=0.38, real=28.13 secs]

                                    這是在發生YGC後,並且JVM判斷old區有足夠的空間容納晉升對象,可是在晉升的過程當中,因爲old區的內存碎片,致使的晉升失敗,而後進行了FULL-GC. 因爲這個過程是發送YGC失敗致使的,因此耗時比並發模式失敗還要多。

        十永久代或者元空間用盡致使的full-gc

                                            279.803: [Full GC 279.803:

                     [CMS: 88569K->68870K(1398144K), 0.6714090 secs]

                     558070K->68870K(2027264K),

                     [CMS Perm : 81919K->77654K(81920K)],

                     0.6716570 secs]

 

 

G1:  G1對堆內存進所有行了分區(region),一個代的空間裏的分區並非連續的。有些分區屬於老年代,有些分區屬於年輕代。年輕代的gc和其餘回收器是同樣的,只因此也進行分區是由於能夠更方便的進行分區的大小調整。老年代的gc是對垃圾最多的分區進行清理,這樣可使用更少的時間來達到最好的清理效果。因此對於比較大的堆,G1的停頓時間是要比CMS好的。 G1的老年代清理是把一個region

      G1的4個操做:

      1.新生代垃圾的收集 :並行收集

      2.後臺收集,併發週期:對垃圾最多的分區進行標記,期間至少會發生一次YGC

      3.混合式垃圾收集

      4.必要的full-gc 

 

      併發週期:

            a. 50.541: [GC pause (young) (initial-mark), 0.27767100 secs]

             [Eden: 1220M(1220M)->0B(1220M)

             Survivors: 144M->144M Heap: 3242M(4096M)->2093M(4096M)]

         [Times: user=1.02 sys=0.04, real=0.28 secs]

        併發週期的開始—初始化標記。這個動做是STW的,這裏使用了YGC來進行STW,

    b. 50.819: [GC concurrent-root-region-scan-start] 

                     51.408: [GC concurrent-root-region-scan-end, 0.5890230]

掃描根分區,這個過程是併發進行的。可是,這個階段中,不能進行YGC,若是這個時候觸發了YGC,YGC必須等待這個動做的結束,而後再進行。這致使了YGC時間變長,意味着須要進行調優了,因此就會有下面這種日誌。

    350.994: [GC pause (young)

              351.093: [GC concurrent-root-region-scan-end, 0.6100090]

              351.093: [GC concurrent-mark-start],

              0.37559600 secs]

    c.  350.994: [GC pause (young)

              351.093: [GC concurrent-root-region-scan-end, 0.6100090]

              351.093: [GC concurrent-mark-start],

              0.37559600 secs]

併發標記,這個過程是能夠中斷的,中間是能夠有YGC併發進行的。

    d.   120.910: [GC remark 120.959:

              [GC ref-PRC, 0.0000890 secs], 0.0718990 secs]

              [Times: user=0.23 sys=0.01, real=0.08 secs]

      120.985: [GC cleanup 3510M->3434M(4096M), 0.0111040 secs]

              [Times: user=0.04 sys=0.00, real=0.01 secs]

從新標記和清理階段。這倆個階段是STW的,並且這個清理階段只會清理不多的垃圾,主要仍是對垃圾的標記。

  e.   120.996: [GC concurrent-cleanup-start]

               120.996: [GC concurrent-cleanup-end, 0.0004520]

一個併發清理,一樣回收的垃圾不多。

       

        混合式垃圾收集週期:

           這個週期會進行YGC和老年代的併發清理。這是stw的

           a.  79.826: [GC pause (mixed), 0.26161600 secs]

     ....

        [Eden: 1222M(1222M)->0B(1220M)

           Survivors: 142M->144M Heap: 3200M(4096M)->1964M(4096M)]

        [Times: user=1.01 sys=0.00, real=0.26 secs]

          混合式垃圾回收週期會進行多輪,直到知足咱們設定的指標。而後整個老年代的回收週期就結束了。

 

        FULL-GC的發生:

1. 併發模式失敗:老年代開始了標記週期,可是在標記週期完成以前就被填滿了。這種狀況下,G1日誌裏會有放棄標記週期的日誌:

                  51.408: [GC concurrent-mark-start]

         65.473: [Full GC 4095M->1395M(4096M), 6.1963770 secs]

          [Times: user=7.87 sys=0.00, real=6.20 secs]

         71.669: [GC concurrent-mark-abort]

2. 晉升失敗:老年代已經開始了mix-gc週期,可是老年代在垃圾回收釋放出足夠的內存以前就被耗盡了。這種狀況是mix-gc以後立刻就有一次Full-Gc

      2226.224: [GC pause (mixed)

               2226.440: [SoftReference, 0 refs, 0.0000060 secs]

               2226.441: [WeakReference, 0 refs, 0.0000020 secs]

               2226.441: [FinalReference, 0 refs, 0.0000010 secs]

               2226.441: [PhantomReference, 0 refs, 0.0000010 secs]

               2226.441: [JNI Weak Reference, 0.0000030 secs]

                       (to-space exhausted), 0.2390040 secs]

       ....

           [Eden: 0.0B(400.0M)->0.0B(400.0M)

               Survivors: 0.0B->0.0B Heap: 2006.4M(2048.0M)->2006.4M(2048.0M)]

           [Times: user=1.70 sys=0.04, real=0.26 secs]

       2226.510: [Full GC (Allocation Failure)

               2227.519: [SoftReference, 4329 refs, 0.0005520 secs]

               2227.520: [WeakReference, 12646 refs, 0.0010510 secs]

               2227.521: [FinalReference, 7538 refs, 0.0005660 secs]

               2227.521: [PhantomReference, 168 refs, 0.0000120 secs]

               2227.521: [JNI Weak Reference, 0.0000020 secs]

                       2006M->907M(2048M), 4.1615450 secs]

           [Times: user=6.76 sys=0.01, real=4.16 secs]

3.疏散失敗:在新生代的回收中,survivor區和老年代沒有足夠的內存來容納晉升和存活的對象,這時會有一個比較特別的日誌:

60.238: [GC pause (young) (to-space overflow), 0.41546900 secs]

4.巨型對象分配失敗:沒有什麼好的辦法來排查這種問題,若是出現了比較奇怪的full-gc,能夠考慮這種狀況。

相關文章
相關標籤/搜索