天天看點

JVM 篇:HotSpot GC

注:HotSpot 是 JDK 自帶的虛拟機類型

如果對垃圾回收的基本概念不是很了解,可以點選 GC 基本概念 前往了解。

HotSpot 使用可達性分析算法來獲知哪些對象是有用的,然後把沒用的對象回收掉。具體過程為:

  1. GC 時程式中各線程會跑到 安全區域 裡面離自己最近的 安全點 更新 OopMap 的資料(棧幀中引用的位置),然後阻塞自己
  2. 通過 OopMap 記錄的引用位置快速的枚舉 GC Roots,然後開始可達性分析
  3. 使用垃圾收集器回收記憶體

垃圾收集器

JVM 篇:HotSpot GC
  • Young generation 表示新生代,Tenured generation 表示老年代
  • 圖中有連線關聯的表示可以配合使用。
    • 從上圖可以知道 G1 是唯一一個可以同時處理新生代和老年代的收集器,其他的都要有一個新生代的收集器和一個老年代的收集器配合。
    • 可以看到老年代中 CMS 和 Serial Old 有交集,意思是當 CMS 收集失敗的時候會使用 Serial Old 來代替。
新生代收集器
  • Serial:停止所有使用者線程(别名 Stop the world),使用一條 GC 線程來進行垃圾回收。
  • ParNew:Serial 的更新版,使用多條 GC 線程來回收。
  • Parallel Scavenge:過程和 ParNew 一模一樣,不同在于這是以吞吐量優先的收集器。
老年代收集器
  • Serial Old:Serial 的老年代版本,Serial / Serial Old 是 client 模式的标配(使用者桌面程式,記憶體一般很小)。
  • Parallel Old:Parallel Scavenge 的老年代版本,Paraller / Parallel Old 是吞吐量優先的收集器配套方案。
  • CMS(Concurrent Mark Sweep):JDK7 和 JDK8 的預設老年代收集器。特點:并發收集,低停頓。
    • CMS 工作過程:
      • 初始标記:标記 GC Roots 能直接關聯的對象,需要 Stop the world。
      • 并發标記:GC Roots Tracing,與使用者線程同時執行。
      • 重新标記:對并發标記的矯正(因為與使用者線程同時執行,可能會發生引用變化的情況),需要 Stop the world。
      • 并發清除:開始回收垃圾,與使用者線程同時執行。
    • 缺點:
      • 通過名字可以得知,内部使用的是 标記-清除算法,是以會産生很多記憶體碎片。
      • 因為最後一步是 并發清除,是以會産生許多浮動垃圾(并發的使用者線程産生的垃圾),這部分垃圾需要等到下一次 Full GC 才能夠回收。同時因為有這些浮動垃圾的存在,老年代需要預留一些記憶體來存放,是以 CMS 不能像其他收集器一樣等到老年代區域滿了之後才開始 GC。
G1
  • JDK6 的時候開始研發,JDK7 的時候才開始商用,計劃用來取代 CMS。
  • G1 收集的範圍是整個堆,堆被分成多個獨立的區域(region),雖然還保留着新生代和老年代的差別,但是它們不再是實體上的隔離了。
  • G1 的一個特色就是它可以建立可預測的停頓,G1 會維護一張清單記錄 region 的回收優先級(越有回收價值的 region 優先級越高),是以可以在規定的停頓範圍内回收最具有回收價值的區域,Garbage First 就是 G1 名字的由來。
  • G1 把記憶體化整為零後,需要靠 RememberSet 來避免全堆掃描(即回收新生代記憶體時不會掃描老年代的區域)。
  • G1 的工作工程與 CMS 很相似,隻有最後一步有明顯的差別,CMS 最後一步是并發清除,而 G1 是篩選回收,會發生 Stop the world,使用多線程來執行回收,好處是不會産生浮動垃圾。
  • G1 采用 複制算法 來回收新生代,使用 标記-整理算法 來回收老年代,是以不會産生記憶體碎片。

記憶體配置設定與回收政策

  • 對象優先在 Eden 區配置設定
  • 大對象直接進入老年代(由 -XX:PretenureSizeThreshold 參數決定大小閥值)
  • 長期存活的對象進入老年代(GC 年齡,由 -XX:MaxTenuringThreshold 決定年齡閥值)
  • 動态對象年齡判斷(當同一年齡的對象的大小超過 survivor 區的一半,則大于或小于這個年齡的就會直接進入老年代)
  • 空間配置設定擔保(survivor 區域不足以存放 GC 後存活的對象時,survivor 中原先的對象就會進入老年代,然後再存放此次 GC 後的對象)

雜談:

  • 新生代使用 複制算法 來進行垃圾回收,是以不會産生記憶體碎片;老年代隻有 CMS 采用了 标記-清除算法,是以隻有它會産生記憶體碎片。
  • 新生代的垃圾收集被稱為是 Minor GC,而老年代被稱為 Major GC / Full GC。後者比前者慢 10 倍以上。
  • 使用 System.gc(); 顯式發動 GC,使用 -XX:PrintGCDetails 可以檢視記憶體回收的情況。

  以上内容為閱讀 深入了解Java虛拟機(第2版)後的筆記及對 JDK8 的實踐補充。看完這本書後最大的感覺就是,,,再看一遍,很多原來了解不了的知識點就可以看懂了,因為很多内容是前後呼應的。有興趣的可以去閱讀這本書,強推。