Garbage First介紹

          本文摘自《構建高性能的大型分布式Java應用》一書,Garbage First簡稱G1,它的目標是要做到盡量減少GC所導致的應用暫停的時間,讓應用達到準實時的效果,同時保持JVM堆空間的利用率,將作為CMS的替代者在JDK 7中閃亮登場,其最大的特色在于允許指定在某個時間段內GC所導致的應用暫停的時間最大為多少,例如在100秒內最多允許GC導致的應用暫停時間為1秒,這個特性對于準實時響應的系統而言非常的吸引人,這樣就再也不用擔心系統突然會暫停個兩三秒了。

          G1要做到這樣的效果,也是有前提的,一方面是硬件環境的要求,必須是多核的CPU以及較大的內存(從規范來看,512M以上就滿足條件了),另外一方面是需要接受吞吐量的稍微降低,對于實時性要求高的系統而言,這點應該是可以接受的。

          為了能夠達到這樣的效果,G1在原有的各種GC策略上進行了吸收和改進,在G1中可以看到增量收集器和CMS的影子,但它不僅僅是吸收原有GC策略的優點,并在此基礎上做出了很多的改進,簡單來說,G1吸收了增量GC以及CMS的精髓,將整個jvm Heap劃分為多個固定大小的region,掃描時采用Snapshot-at-the-beginning的并發marking算法(具體在后面內容詳細解釋)對整個heap中的region進行mark,回收時根據region中活躍對象的bytes進行排序,首先回收活躍對象bytes小以及回收耗時短(預估出來的時間)的region,回收的方法為將此region中的活躍對象復制到另外的region中,根據指定的GC所能占用的時間來估算能回收多少region,這點和以前版本的Full GC時得處理整個heap非常不同,這樣就做到了能夠盡量短時間的暫停應用,又能回收內存,由于這種策略在回收時首先回收的是垃圾對象所占空間最多的region,因此稱為Garbage First

          看完上面對于G1策略的簡短描述,并不能清楚的掌握G1,在繼續詳細看G1的步驟之前,必須先明白G1對于JVM Heap的改造,這些對于習慣了劃分為new generationold generation的大家來說都有不少的新意。

          G1Heap劃分為多個固定大小的region,這也是G1能夠實現控制GC導致的應用暫停時間的前提,region之間的對象引用通過remembered set來維護,每個region都有一個remembered setremembered set中包含了引用當前region中對象的region的對象的pointer,由于同時應用也會造成這些region中對象的引用關系不斷的發生改變,G1采用了Card Table來用于應用通知region修改remembered setsCard Table由多個512字節的Card構成,這些CardCard Table中以1個字節來標識,每個應用的線程都有一個關聯的remembered set log,用于緩存和順序化線程運行時造成的對于card的修改,另外,還有一個全局的filled RS buffers,當應用線程執行時修改了card后,如果造成的改變僅為同一region中的對象之間的關聯,則不記錄remembered set log,如造成的改變為跨region中的對象的關聯,則記錄到線程的remembered set log,如線程的remembered set log滿了,則放入全局的filled RS buffers中,線程自身則重新創建一個新的remembered set logremembered set本身也是一個由一堆cards構成的哈希表。

          盡管G1Heap劃分為了多個region,但其默認采用的仍然是分代的方式,只是僅簡單的劃分為了年輕代(young)和非年輕代,這也是由于G1仍然堅信大多數新創建的對象都是不需要長的生命周期的,對于應用新創建的對象,G1將其放入標識為youngregion中,對于這些region,并不記錄remembered set logs,掃描時只需掃描活躍的對象,G1在分代的方式上還可更細的劃分為:fully youngpartially youngfully young方式暫停的時候僅處理young regionspartially同樣處理所有的young regions,但它還會根據允許的GC的暫停時間來決定是否要加入其他的非young regionsG1是運行到fully-young方式還是partially young方式,外部是不能決定的,在啟動時,G1采用的為fully-young方式,當G1完成一次Concurrent Marking后,則切換為partially young方式,隨后G1跟蹤每次回收的效率,如果回收fully-young中的regions已經可以滿足內存需要的話,那么就切換回fully young方式,但當heap size的大小接近滿的情況下,G1會切換到partially young方式,以保證能提供足夠的內存空間給應用使用。

          除了分代方式的劃分外,G1還支持另外一種pure G1的方式,也就是不進行代的劃分,pure方式和分代方式的具體不同在下面的具體執行步驟中進行描述。

          掌握了這些概念后,繼續來看G1的具體執行步驟:

          1.         Initial Marking

          G1對于每個region都保存了兩個標識用的bitmap,一個為previous marking bitmap,一個為next marking bitmapbitmap中包含了一個bit的地址信息來指向對象的起始點。

          開始Initial Marking之前,首先并發的清空next marking bitmap,然后停止所有應用線程,并掃描標識出每個regionroot可直接訪問到的對象,將regiontop的值放入next top at mark startTAMS)中,之后恢復所有應用線程。

          觸發這個步驟執行的條件為:

          l  G1定義了一個JVM Heap大小的百分比的閥值,稱為h,另外還有一個HH的值為(1-h)*Heap Size,目前這個h的值是固定的,后續G1也許會將其改為動態的,根據jvm的運行情況來動態的調整,在分代方式下,G1還定義了一個u以及soft limitsoft limit的值為H-u*Heap Size,當Heap中使用的內存超過了soft limit值時,就會在一次clean up執行完畢后在應用允許的GC暫停時間范圍內盡快的執行此步驟;

          l  pure方式下,G1markingclean up組成一個環,以便clean up能充分的使用marking的信息,當clean up開始回收時,首先回收能夠帶來最多內存空間的regions,當經過多次的clean up,回收到沒多少空間的regions時,G1重新初始化一個新的markingclean up構成的環。

          2.         Concurrent Marking

          按照之前Initial Marking掃描到的對象進行遍歷,以識別這些對象的下層對象的活躍狀態,對于在此期間應用線程并發修改的對象的以來關系則記錄到remembered set logs中,新創建的對象則放入比top值更高的地址區間中,這些新創建的對象默認狀態即為活躍的,同時修改top值。

          3.         Final Marking Pause

          當應用線程的remembered set logs未滿時,是不會放入filled RS buffers中的,在這樣的情況下,這些remebered set logs中記錄的card的修改就會被更新了,因此需要這一步,這一步要做的就是把應用線程中存在的remembered set logs的內容進行處理,并相應的修改remembered sets,這一步需要暫停應用,并行的運行。

          4.         Live Data Counting and Cleanup

          值得注意的是,在G1中,并不是說Final Marking Pause執行完了,就肯定執行Cleanup這步的,由于這步需要暫停應用,G1為了能夠達到準實時的要求,需要根據用戶指定的最大的GC造成的暫停時間來合理的規劃什么時候執行Cleanup,另外還有幾種情況也是會觸發這個步驟的執行的:

          l  G1采用的是復制方法來進行收集,必須保證每次的”to space”的空間都是夠的,因此G1采取的策略是當已經使用的內存空間達到了H時,就執行Cleanup這個步驟;

          l  對于full-youngpartially-young的分代模式的G1而言,則還有情況會觸發Cleanup的執行,full-young模式下,G1根據應用可接受的暫停時間、回收young regions需要消耗的時間來估算出一個yound regions的數量值,當JVM中分配對象的young regions的數量達到此值時,Cleanup就會執行;partially-young模式下,則會盡量頻繁的在應用可接受的暫停時間范圍內執行Cleanup,并最大限度的去執行non-young regionsCleanup

          這一步中GC線程并行的掃描所有region,計算每個region中低于next TAMS值中marked data的大小,然后根據應用所期望的GC的短延時以及G1對于region回收所需的耗時的預估,排序region,將其中活躍的對象復制到其他region中。

           

          G1為了能夠盡量的做到準實時的響應,例如估算暫停時間的算法、對于經常被引用的對象的特殊處理等,G1為了能夠讓GC既能夠充分的回收內存,又能夠盡量少的導致應用的暫停,可謂費盡心思,從G1的論文中的性能評測來看效果也是不錯的,不過如果G1能允許開發人員在編寫代碼時指定哪些對象是不用mark的就更完美了,這對于有巨大緩存的應用而言,會有很大的幫助,G1將JDK 6 Update 14 beta發布。

          posted on 2009-03-11 22:18 BlueDavy 閱讀(12565) 評論(4)  編輯  收藏 所屬分類: Java

          評論

          # re: Garbage First介紹 2009-03-14 13:37 phpxer

          Mark  回復  更多評論   

          # re: Garbage First介紹 2009-04-01 12:14 中國兄弟連

          給你踩踩哈!  回復  更多評論   

          # re: Garbage First介紹 2013-03-04 23:52 呵樂貓

          學習了。  回復  更多評論   

          # re: Garbage First介紹 2016-03-01 17:47 mvilplss

          學習啦  回復  更多評論   

          公告

           









          feedsky
          抓蝦
          google reader
          鮮果

          導航

          <2009年3月>
          22232425262728
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          統計

          隨筆分類

          隨筆檔案

          文章檔案

          Blogger's

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 雅江县| 海盐县| 宜昌市| 竹北市| 北京市| 胶南市| 镶黄旗| 于田县| 凤台县| 会理县| 库尔勒市| 邛崃市| 班戈县| 错那县| 资源县| 卓尼县| 扶风县| 邛崃市| 武穴市| 富顺县| 桓台县| 忻州市| 南和县| 井陉县| 察隅县| 临朐县| 收藏| 聂拉木县| 庆元县| 奇台县| 长岛县| 新营市| 尼玛县| 唐海县| 隆昌县| 隆德县| 孙吴县| 滕州市| 平阴县| 蕉岭县| 威远县|