1. 程式人生 > 程式設計 >JVM之垃圾收集(Garbage Collection [Ⅱ])

JVM之垃圾收集(Garbage Collection [Ⅱ])

7 個垃圾收集器

垃圾收集器就是記憶體回收操作的具體實現,HotSpot 有 7 種。因為它們各有各的適用場景。有的屬於新生代收集器,有的屬於老年代收集器,所以一般都是搭配使用的。關於它們的簡單介紹以及分類請見下圖。

Serial 收集器

特點:

1.“單執行緒”操作。會產生“Stop The World”。
2.採用"Stopr the world"。
3.Serial 收集器是虛擬機器器在 Client模式下的預設新生代收集器,它的優勢是簡單高效,適合單 CPU 模式。
複製程式碼

ParNew 收集器

特點:

1.其本身就是 Serial 收集器的多執行緒版本。雖然除此之外沒什麼創新之處,但它卻是許多執行在 Server 模式下的虛擬機器器中的首選新生代收集器。

2.除了 Serial 收集器外,只有它能和 CMS 收集器搭配使用。
複製程式碼

Parallel Scavenge 收集器

特點:

1.新生代收集器,並行的多執行緒收集器。
2.使用複製演演算法,
3.可控制吞吐量(Throughput)。
複製程式碼

吞吐量 = 執行使用者程式碼時間 / ( 執行使用者程式碼時間 + 垃圾收集時間 )

可調節的虛擬機器器引數:

  • -XX:MaxGCPauseMillis:最大 GC 停頓的秒數;
  • -XX:GCTimeRatio:吞吐量大小,一個 0 ~ 100 的數,最大 GC 時間佔總時間的比率 = 1 / (GCTimeRatio + 1)
  • -XX:+UseAdaptiveSizePolicy:一個開關引數,開啟後就無需手工指定 -Xmn
    -XX:SurvivorRatio 等引數了,虛擬機器器會根據當前系統的執行情況收集效能監控資訊,自行調整。

Serial Old 收集器

特點:

1.Serial收集器的老年代版,同為單執行緒。
2.使用標記整理演演算法。
3.作為CMS收集容器的後備預案。
複製程式碼

Prallel Old 收集器

特點:

1.Parallel Old收集器的老年代版,多執行緒。
2.使用標記-整理演演算法。
複製程式碼

CMS 收集器(Concurrent Mark Sweep)

特點:

1.CMS注重於服務的響應速度,希望系統停頓時間最短。
2.基於“標記-清除”演演算法實現的。【注1】
3.存在以下幾點缺點:
    (1)CMS收集器對CPU資源非常敏感。
    (2)CMS收集器無法處理浮動垃圾。
    (3)由於基於標記-清除演演算法,所以會產生大量碎片
複製程式碼

【注1】 標記-清除演演算法步驟: 1.初始標記 2.併發標記 3.重新標記 4.併發清除

引數設定:

  • -XX:+UseCMSCompactAtFullCollection:在 CMS 要進行 Full GC 時進行記憶體碎片整理(預設開啟)
  • -XX:CMSFullGCsBeforeCompaction:在多少次 Full GC 後進行一次空間整理(預設是 0,即每一次 Full GC 後都進行一次空間整理)

關於 CMS 使用 標記 - 清除 演演算法的一點思考:

之前對於 CMS 為什麼要採用 標記 - 清除 演演算法十分的不理解,既然已經有了看起來更高階的 標記 - 整理 演演算法,那 CMS 為什麼不用呢?最近想了想,感覺可能是這個原因,不過也不是很確定,只是個人的一種猜測。

標記 - 整理 會將所有存活物件向一端移動,然後直接清理掉邊界以外的記憶體。這就意味著需要一個指標來維護這個分隔存活物件和無用空間的點,而我們知道 CMS 是併發清理的,雖然我們啟動了多個執行緒進行垃圾回收,不過如果使用 標記 - 整理 演演算法,為了保證執行緒安全,在整理時要對那個分隔指標加鎖,保證同一時刻只有一個執行緒能修改它,加鎖的這一過程相當於將並行的清理過程變成了序列的,也就失去了並行清理的意義了。

所以,CMS 採用了 標記 - 清除 演演算法。

G1 收集器

特點: 1.並行併發。 2.分代收集。 3.空間整合。 4.可預測的停頓。

G1運算步驟

1.初始標記
2.併發標記
3.最終標記
4.篩選回收
複製程式碼

GC 日誌解讀

Java 記憶體分配策略

新生代和老年代的 GC 操作:

  • 新生代 GC 操作:Minor GC
    • 發生的非常頻繁,速度較塊。
  • 老年代 GC 操作:Full GC / Major GC
    • 經常伴隨著至少一次的 Minor GC;
    • 速度一般比 Minor GC 慢上 10 倍以上。

優先在 Eden 區分配

  • Eden 空間不夠將會觸發一次 Minor GC;
  • 虛擬機器器引數:
    • -Xmx:Java 堆的最大值;
    • -Xms:Java 堆的最小值;
    • -Xmn:新生代大小;
    • -XX:SurvivorRatio=8:Eden 區 / Survivor 區 = 8 : 1

大物件直接進入老年代

  • 大物件定義: 需要大量連續記憶體空間的 Java 物件。例如那種很長的字串或者陣列。
  • 設定物件直接進入老年代大小限制:
    • -XX:PretenureSizeThreshold:單位是位元組;
      • 只對 Serial 和 ParNew 兩款收集器有效。
    • 目的: 因為新生代採用的是複製演演算法收集垃圾,大物件直接進入老年代可以避免在 Eden 區和 Survivor 區發生大量的記憶體複製。

長期存活的物件將進入老年代

  • 固定物件年齡判定: 虛擬機器器給每個物件定義一個年齡計數器,物件每在 Survivor 中熬過一次 Minor GC,年齡 +1,達到 -XX:MaxTenuringThreshold 設定值後,會被晉升到老年代,-XX:MaxTenuringThreshold 預設為 15;
  • 動態物件年齡判定: Survivor 中有相同年齡的物件的空間總和大於 Survivor 空間的一半,那麼,年齡大於或等於該年齡的物件直接晉升到老年代。