1. 程式人生 > >JVM系列三(垃圾收集器).

JVM系列三(垃圾收集器).

一、概述

1. 哪些記憶體需要回收

上篇文章 我們介紹了 Java 記憶體執行時區域的各個部分,其中程式計數器、虛擬機器棧、本地方法棧三個區域隨執行緒而生,隨執行緒而滅,在這幾個區域內就不需要過多考慮回收的問題,因為方法結束或者執行緒結束時,記憶體自然就跟著回收了。

而方法區和 Java 堆是執行緒共享的,我們只有在程式處於執行期間才能知道會建立哪些物件,這部分記憶體的分配和回收都是動態的,垃圾收集器所關注的是這部分記憶體。

2. 回收方法區

方法區的垃圾收集主要回收兩部分內容:廢棄常量和無用的類。

“廢棄常量”指的是當前系統中沒有任何一個物件引用指向該常量。

“無用的類”需要同時滿足下面三個條件才有可能被虛擬機器回收,至於最終是否回收還由虛擬機器引數:-Xnoclassgc 控制。

  • 該類的所有例項都已被回收,也就是 Java 堆中不存在該類的任何例項。
  • 載入該類的 ClassLoader 已經被回收。
  • 該類對應的 Class 物件沒有被任何地方被引用,無法在任何地方通過反射訪問該類的介面。

二、垃圾回收器

首先開始之前先看下 HotSpot 虛擬機器所包含的收集器:

圖中展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,則說明它們可以搭配使用。虛擬機器所處的區域則表示它是屬於新生代還是老年代收集器。

1. Serial 收集器

新生代收集器,複製演算法收集,Serial 收集器是最基本、發展歷史最悠久的收集器。它是一個單執行緒的收集器,只會使用一個 CPU 或一條收集執行緒去完成垃圾收集工作,它在垃圾收集時,必須暫停其他所有的工作執行緒,直到它收集結束。

優點:簡單高效;虛擬機器 Client 模式下表現優異(Client 模式下記憶體較小、CPU較少,能減少許多執行緒互動的開銷)。
缺點:回收工作需要 Stop The World ;單執行緒;不適用虛擬機器 Server 模式(Server 模式下記憶體較大、CPU較多,導致回收工作停頓時間過長)。

2. ParNew 收集器

新生代收集器,複製演算法收集,ParNew 收集器其實就是 Serial 收集器的多執行緒版本,除了使用多執行緒進行回收外,其餘行為包括控制引數、收集演算法、Stop The World、物件分配規則、回收策略等都與 Serial 收集器完全一樣。

優點:多執行緒工作;可以與 CMS 收集器搭配工作;虛擬機器 Server 模式下表現優異。

缺點:回收工作需要 Stop The World 。

3. Parallel Scavenge 收集器

新生代收集器,複製演算法收集,多執行緒工作,Parallel Scavenge 收集器的關注點在於達到一個可控制的吞吐量(其他收集器的關注點是縮短垃圾收集時使用者執行緒的停頓時間),停頓時間越短越適合需要與使用者互動的程式;而高吞吐量則可以高效率的利用 CPU 時間,儘快完成程式的執行任務。

GC 自適應調節策略是 Parallel Scavenge 收集器和 ParNew 收集器的一個重要區別。它變現為:只需要把基本的記憶體資料設定好(如 -Xmx 設定最大堆),然後使用 MaxGCPauseMillis 引數(更關注最大停頓時間)或 GCRatio(更關注吞吐量)給虛擬機器設立一個優化目標,那具體細節引數的調節工作就由虛擬機器來完成了。

優點:多執行緒工作;注重系統吞吐量和CPU資源;自適應調節策略。
缺點:回收工作需要 Stop The World ;可選的老年代收集器過少,無法與 CMS 收集器配合工作,在 JDK1.5 之前只能和 Serial Old 收集器配合工作。

tips:

  • 吞吐量 = 執行使用者程式碼時間 / (執行使用者程式碼時間 + 垃圾收集時間)
  • 自適應調節策略使用 -XX:+UseAdptiveSizePolicy 引數開啟。
  • 與吞吐量關係密切,故也稱為“吞吐量優先”收集器。

4. Serial Old 收集器

老年代收集器,標記-整理演算法,單執行緒,Serial Old 收集器是 Serial 收集器的老年代版本。

優點:虛擬機器 Client 模式下表現尚可(Client 模式下記憶體較小、CPU較少,能減少許多執行緒互動的開銷);CMS 收集器的後備預案(在併發收集Concurent Mode Failure時使用)。
缺點:回收工作需要 Stop The World ;單執行緒。

5. Parallel Old 收集器

老年代收集器,標記-整理演算法,多執行緒,Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,在 JDK1.6 後開始提供。

優點:搭配 Parallel Scavenge 收集器使用,關注系統吞吐量以及CPU資源。
缺點:回收工作需要 Stop The World ;可搭配的新生代收集器僅有 Parallel Scavenge 收集器而已。

6. CMS 收集器

老年代收集器,標記-清除演算法,多執行緒,CMS(Concurrent Mark Sweep)收集器是一種以獲得最短回收停頓時間為目標的收集器,是真正意義上與使用者執行緒併發執行的收集器,因此,使用 CMS 收集器能給使用者帶來良好的體驗。

優點:併發收集;低停頓。
缺點:

  1. CMS 收集器對 CPU 資源敏感,在併發標記/清理 的時候,雖然不會導致使用者執行緒停頓,但標記/清理工作是要佔用一部分 CPU 資源的,這無疑會降低吞吐量。(CMS 預設啟動的回收執行緒數是 (CPU 數量 + 3)/ 4)
  2. CMS 收集器無法處理浮動垃圾(Floating Garbage),可能出現 “Concurent Mode Failure” 失敗而導致另一次 Full GC 的產生(使用 Serial Old 收集器)。浮動垃圾指的是併發清理階段,使用者執行緒併發執行產生的垃圾,當這些浮動垃圾的記憶體超過了CMS 執行期間預留的記憶體,就會導致 “Concurent Mode Failure” 失敗。
  3. CMS 收集器使用的標記-清除演算法會有大量的記憶體碎片出現,將會給大物件分配帶來很多麻煩。

7. G1 收集器

分割槽(Region)收集器,標記-整理演算法和複製演算法,多執行緒,G1(Garbage-First)收集器在 JDK 7u4 版本釋出,在 JDK9 中成為預設垃圾收集器,是一款面向服務端應用的垃圾收集器,它的目標也是獲得最短停頓時間。

優點:

  1. 並行和併發,縮短 Stop The World 停頓的時間。
  2. 標記-整理演算法、複製演算法不會出現類似 CMS 的記憶體碎片問題。
  3. 可預測的停頓時間模型,能讓使用者明確指定在一個長度為 M 毫秒的時間片段內,消耗在垃圾收集上的時間不超過 N 毫秒。

推薦場景:

G1的首要目的是為那些需要大容量記憶體和較小 GC 延遲的應用程式提供解決方案。這通常是指那些堆大小設定在 6GB 以上,確定的、可以預測的暫停時間在 0.5 秒以內的應用程式。

如果應用程式符合以下一項或者多項特徵,那麼從 CMS 或者 ParallelOld 收集器切換到 G1 可能更合適。

  • 活動物件佔據了超過 50% 的 Java 堆空間。
  • 物件分配率或者提升率波動明顯。
  • 不希望有長時間的垃圾收集暫停時間(超過0.5秒或1秒)。



參考連結:

  1. 《深入理解 JVM 虛擬機器》
  2. G1垃圾收集器介紹
  3. jvm垃圾收集器(終結篇)