您好,我是湘王,這是我的頭條号「湘王說」,歡迎您來,歡迎您再來~
G1替代了ParNew+CMS這對搭檔組合,既能實作年輕代的垃圾回收,也能實作老年代的垃圾回收。現在繼續來說說它的混合回收問題。
在JVM參數中,有一個設定項-XX:InitiatingHeapOccupancyPercent,它的預設值是45%,也就是-XX:InitiatingHeapOccupancyPercent=45。也就是說,如果老年代空間 ≥ JVM堆記憶體大小 × 45%,就會觸發混合回收。
例如,當JVM有2048個Region,而老年代Region≈1000時,就觸發混合回收。
G1的混合回收,也有這麼幾個步驟:
1、初始标記階段:Stop the World,僅僅隻是标記一下GC Roots直接能應用的對象。
2、并發标記階段:恢複系統程式運作,同時進行GC Roots追蹤所有的存活對象,比較耗時,同時将對象的做出的修改記錄下來。
3、最終标記階段:再次Stop the World,最終标記哪些是存活對象,哪些是垃圾對象。
4、混合回收階段:先計算老年代中每個Region裡面的存活對象數量,占比,GC的預期性能及效率。然後停止系統,全力以赴盡快進行垃圾回收,同時控制GC停頓時間,滿足預期目标。
在混合回收階段,不僅僅回收老年代,也會同時回收年輕代和大對象。從年輕代、老年代、大對象中各自挑選一些Region,在滿足性能前提下(允許GC卡頓的時間),回收盡可能多的垃圾對象。
一些常見G1的參數:
l -XX:G1MixedGCCountTarget=8,在一次混合回收過程中,最後一個階段(混合回收階段)執行幾次回收動作,預設8次;
l -XX:G1HeapWastePercent=5,一旦回收執行完成,發現空閑出來的Region數量達到JVM的5%,就立即停止回收;
l -XX:G1MixedGCLiveThresholdPercent=85,當某個Region的存活對象低于85的時候,這個Region才能進行回收。
而一旦混合回收失敗,就會馬上執行下面的動作(可以了解為類似于事務復原):
l 停止系統運作;
l 采用單線程進行标記、清理、壓縮和整理;
l 回收垃圾,空閑出一批Region;
l 恢複混合回收過程。
有這麼一個G1案例:
l 某百萬級注冊使用者的線上教育平台,DAU≈60萬;
l 選課、排課、課程詳情都屬于低頻浏覽頁面;
l 上課使用頻次最高;
l 99%的流量都集中在每天18:00~21:00;
l 會有大量的遊戲互動環節,即大量點選事件、請求和結果。
系統壓力:
l 3小時60萬使用者,即每1小時20萬使用者;
l 每1分鐘1次互動,則1小時1使用者互動60次;
l 1小時所有使用者就有1200萬次互動,平均每1秒3333次互動,按3500計算;
l 大緻需要7台4C8G機器,平均每1台每1秒扛500次請求;
l 1次請求大概建立10KB對象,500次就是5MB。
G1的預設記憶體布局:
l JVM4G,使用G1垃圾收集器,棧記憶體1M,元空間256M,年輕代預設占用5%空間,最大60%;
l 單個Region大小 = 4096M / 2048 = 2M;
l 年輕代占5% = 2048 × 0.05 ≈ 100 × 2M ≈ 200M;
l 年輕代占60% = 2048 × 0.6 ≈ 1228 × 2M ≈ 2456M;
l 200M / 5M/秒 = 40秒,2456M / 5M/秒 = 491秒。
觸發年輕代GC的時機:假設G1回收200個Region(≈400M)需要200ms,那麼年輕代初始占用的5%記憶體空間,不到1分鐘就會被填滿。如果此時觸發GC,則會出現:每不到1分鐘GC就被觸發1次,雖然隻有200ms的短暫停頓。是以,與其讓GC在200M的年輕代空間裡被頻繁觸發,不如在更大的空間裡,減少GC觸發的頻率。如果此時400個Region都被占滿,GC回收一次大概200ms,那麼這個成本效益顯然比回收100個Region高出4倍。
和ParNew和CMS不同,G1更喜歡大記憶體,配置設定G1參數時,往往會以GB為機關,而不是MB。是以需要給JVM足夠的記憶體空間,結合壓測、日志、記憶體等工具合理地設定-XX:MaxGCPauseMillis的值。
感謝您的大駕光臨!咨詢技術、産品、營運和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~