JVM 篇:HotSpot GC

注:HotSpot 是 JDK 自帶的虛擬機類型
如果對垃圾回收的基本概念不是很瞭解,可以點擊 GC 基本概念 前往瞭解。

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

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

垃圾收集器

在這裏插入圖片描述

  • 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 的實踐補充。看完這本書後最大的感覺就是,,,再看一遍,很多原來理解不了的知識點就可以看懂了,因爲很多內容是前後呼應的。有興趣的可以去閱讀這本書,強推。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章