天天看點

Hotspot虛拟機- 垃圾收集算法和垃圾收集器引言什麼是垃圾收集?Mark and Sweep理想的垃圾收集器特性分代收集(Generational Collection)相關概念垃圾收集算法的種類性能名額垃圾收集類型Hotspot虛拟機中的垃圾收集器總結

引言

聲明:由于CSDN不支援Hexo支援的Markdown文法,大家看到{% asset_img 1st.png Mark and Sweep %}這樣的标簽時,就是一張圖檔。由于圖檔較多,我就不一一複制到這了,大家想看圖檔的話,參考我的原文:Hotspot虛拟機- 垃圾收集算法和垃圾收集器

當提到Java虛拟機的時候,我們首先應該區分2件事情。一個是抽象的Java虛拟機規範,另一個是根據這個規範具體的實作。市面上有很多根據這個規範實作的虛拟機,比如jRockit、IBM J9、Hotspot等。那麼在這篇文章中,我隻介紹關于Hotspot虛拟機的行為,以及它裡面提供的垃圾收集器。

什麼是垃圾收集?

相信每一個現實中生活的人都知道什麼是垃圾收集。顧名思義,就是找到沒有用的東西(垃圾)并把它扔掉。但是,在JVM中的垃圾收集是完全相反的,它首先找到所有仍然使用的對象并标記下來,然後清理掉沒有标記的垃圾。

那麼對于一個垃圾收集器來說,它主要有三個工作需要完成:

  1. 配置設定記憶體
  2. 確定任何被引用的對象在記憶體中,不被垃圾收集器收集
  3. 釋放不再使用的對象的記憶體

被引用的對象我們稱它是活着的(live),不再被引用的對象我們稱它為死的(dead),或垃圾。找到并釋放這些垃圾的過程叫做垃圾收集。

Mark and Sweep

  • Marking : 周遊所有從GC roots開始可到達的對象并把這些對象标記為存活的對象
  • Sweeping : 確定不可達對象占用的記憶體能在下一次記憶體配置設定中可以被重新利用。

在Java中,GC Roots對象可以為:

  • Local variable and input parameters of the currently executing methods
  • Active threads
  • Static fields
  • JNI references

JVM中不同的垃圾收集算法實作細節有些不同,但是大體上來說,所有的垃圾收集算法都遵循着Marking和Sweeping,等我下面介紹這些垃圾收集算法和垃圾收集器的時候大家就能感覺到了。

{% asset_img 1st.png Mark and Sweep %}

相比于Reference Counting來說,可達性分析也可以收集循環引用的垃圾,如上圖所示。由于Hotspot虛拟機并沒有用Reference Counting來判斷一個對象是否存活,這裡我就不介紹這種方法了,大家可以參考一下維基百科上的解釋Reference Counting,概念也很簡單。

理想的垃圾收集器特性

在我介紹Hotspot虛拟機中具體的垃圾收集器之前,我們應該首先了解一個理想的垃圾收集器應該具有什麼樣的特點才能滿足非常複雜的垃圾收集過程。

一、垃圾收集器應該是安全的。也就是說,live對象占用的記憶體不能被釋放,垃圾對象占用的記憶體要盡快釋放。這個特點是最重要的一點,如果不能滿足這點,其它特點再牛逼,也可以say goodbye了。

二、垃圾收集器應該有效地收集垃圾。也就是說:不能讓我們自己的應用在垃圾收集的過程中停頓太長的時間(stop the world)。然而,這就象很多計算機系統一樣,通常在時間、空間和頻率上有一個trade-offs. 比如,如果垃圾收集器收集的堆容量很小,那麼收集過程會很快,但是,堆很快就會被填滿,就會需要更加頻繁的垃圾收集。反之,在堆容量很大的情況下,需要更長的時間才能填滿堆,是以垃圾收集的次數很少,但是每次收集就需要更多的時間。

三、垃圾收集器應該有效解決fragmentation的問題。fragmentation的意思就是說,垃圾收集器在收集完垃圾以後會産生很多不連續的記憶體空間,如果下次配置設定一個大對象有可能出現沒有任何一個連續的空間能容納這個對象,這會導緻記憶體空間的浪費,同時也會觸發下一次垃圾收集過程。有一個方法可以消除fragmentation,它叫做compaction,下面我會介紹的。

四、可擴充性也是很重要的一個特點。 由于現在很多應用中都是多核處理器或者多個處理器,那麼我們的垃圾收集器可以利用這些優勢去并行配置設定記憶體和收集垃圾。

{% asset_img 2nd.png compaction %}

分代收集(Generational Collection)相關概念

在Java8的HotSpot虛拟機中一共包括了5個垃圾收集器,它們每一個都是基于分代收集的思想。在這一節中,我主要介紹一下各個分代區域以及對象是怎樣被配置設定到這些區域的。這是官方文檔給出的5個可得到的收集器:5 Available Collectors,并介紹了如何針對自己的應用選擇出一個合适的收集器。

Generational Hypothesis

對于Generational Hypothesis的概念,Jeff Hammerbacher在Quora上已經給出一個很好地答案,我把它翻譯一下。

Generational Hypothesis是一個關于對象生命周期分布的假設。準确地說,這個假設認為對象生命周期的分布是雙峰的:大部分的對象要麼是短命的,要麼就是一直存活的。

{% asset_img 3rd.png Generational Hypothesis %}

基于這個假設,Hotspot虛拟機把記憶體分為年輕代(Young Generation)和老年代(Old Generation)。有了這樣的記憶體區域劃分,我們可以針對不同的區域選擇合适的算法來進行垃圾收集,進而大大提高垃圾收集的效率。注意:分代收集是基于上面的假設來進行的,如果你的應用完全不符合上面的假設,那麼你的垃圾收集效率一定很低。

因為年輕代空間通常很小,包含很多短命的對象,是以它的收集要頻繁一些。經過了幾輪年輕代收集,依然存活的對象被晉升(promoted)或者tenured到老年代。因為老年代的空間要比年輕代大很多并且它的對象大部分都是長命的,是以它的收集是不頻繁的。由于年輕代的收集很頻繁,是以針對這個區域的收集算法要很快。另一方面,由于老年代的收集不是很頻繁的并且它占用了大多數的堆空間,是以這一區域的算法針對低頻的垃圾收集要空間有效的。

在介紹各個分代區域之前,大家先看看下面這張圖。

{% asset_img 4th.png Generational Collection %}

注意:在Java 8中已經移除了永久代。

年輕代

年輕代是由一個Eden區域 + 2個survivor區域組成。大部分的對象最初都被分到Eden區域(特别大的對象可能直接被配置設定到老年代)。對于2個survivor區域來說,它們中的一個必須始終是空的。并且每個survivor區域中的對象至少是經曆過一次年輕代垃圾收集的。假設幾分鐘前垃圾收集器已經進行了一次年輕代的垃圾收集了,Eden區域和其中的1個survivor區域都有對象,另一個survivor區域為空。現在,又要進行一次垃圾收集了,收集器做的就是:把Eden區域和那個有對象的survivor區域中活着的對象找出來并複制到另一個空的survivor區域中,然後清空Eden區域和先前有對象的那個survivor區域。

如果空的這個survivor區域的空間不夠裝下Eden區域和另一個survivor區域中活着的對象,那麼收集器會把容納不下的對象直接配置設定到老年代。如果老年代也容不下這些對象,那麼會觸發老年代的垃圾收集,然後去容納這些對象。

由于Java應用支援多線程,那麼在多線程應用的情況下對象的配置設定就會出現一些問題。比如,我上一個線程配置設定的對象會被下一個線程所配置設定的對象覆寫。如果用一個全局鎖來把整個年輕代鎖住,那麼配置設定一個對象到年輕代的性能會非常低下。是以,虛拟機團隊想出了一個解決方案叫做Thread-Local Allocation Buffers (TLABs).

{% asset_img 5th.png Thread-Local Allocation Buffers %}

如上圖所示,每一個線程都有一個自己的TLAB,配置設定對象時用指針碰撞(bump-the-pointer)技術去在各自的TLAB中配置設定對象,大大提升了年輕代配置設定對象的效率。設定‐XX:+UseTLAB來啟用TLAB,通過‐XX:TLABSize來設定其大小,預設大小為0,0表示虛拟機動态計算其大小。

經過了幾次垃圾收集還沒有被回收的對象就被promoted到老年代了。那麼如何去判斷一個對象是否足夠老可以晉升到老年代呢?垃圾收集器追蹤每個活着對象被收集的次數,每挺過一次垃圾收集,對象的年齡就加1,當一個對象的年齡超過了指定的阙值(tenuring threshold),它就會被晉升到老年代。通過設定XX:+MaxTenuringThreshold來指定一個上限,如果設定為0,那麼經過1次垃圾收集以後馬上被晉升。

老年代

老年代的空間是非常大的并且它裡面存在的對象成為垃圾的可能性很小。老年代的垃圾收集次數要比年輕代少很多,并且由于老年代的對象很少會成為垃圾對象,年輕代的做法(在survivor區域不斷copy)并不适合老年代。老年代具體的收集算法我會在下面具體的垃圾收集器中介紹。

永久代

永久代在Java 8以前存在。 JVM用這裡存儲一些類的中繼資料還有一些被内在化的字元串。What is String interning?詳細地解釋了什麼是内在化字元串。Hotspot虛拟機用永久代實作了方法區,是以如果你用動态代理技術或CGLib産生大量的增強代理類,都會使永久代出現異常。比如,當你用Spring的AOP時,它都會為想要增強的類産生一個代理類進而達到增強的目的,如果産生的類很多,你的永久代将會溢出。

永久代給Java開發者制造了很多的麻煩,因為很難預測出它将需要多少記憶體空間。如果出現溢出:産生java.lang.OutOfMemoryError: Permgen space.的錯誤。

Metaspace

由于上面永久代的缺點,它在Java 8中被移除,取而代之的是Metaspace,這塊記憶體區域位于本地記憶體中。預設情況下,Metaspace的大小隻被Java程序可得到的本地記憶體所限制。是以,這個區域并不會因為稍微增加一個類就導緻溢出。注意:Metaspace沒有限制地增長将會導緻本地記憶體溢出。 你可以設定-XX:MaxMetaspaceSize來限制其大小。

垃圾收集算法的種類

從上面的分類收集中我們可以看出,不同的分代區域具有不同的特點,是以我們可以利用這些特點來選出具有針對性地垃圾收集算法,使得收集更加有效。本節我會介紹Hotspot虛拟機中的垃圾收集器會用到的算法,介紹它們具體的實作過程。

Serial vs Parallel

即使你的電腦有多個CPU,Serial隻用一個CPU來進行垃圾收集。對于Parallel來說,它會把一個垃圾收集任務拆分成幾部分,在多個CPU上并行執行這些任務。Parallel收集将會使垃圾收集更快地完成,但是,它同時也增加了額外的複雜性和潛在的fragmentation.

Concurrent versus Stop-the-world

對于Stop-the-world來說,在垃圾收集期間,我們自己的應用完全被停止。對于Concurrent來說,它可以并發的執行垃圾收集和我們的應用。Stop-the-world垃圾收集要比Concurrent垃圾收集簡單的多,因為我們自己的應用不需要繼續運作,那麼在垃圾收集期間就不會向堆中加入對象,這大大簡化了垃圾收集的過程。“成也蕭何,敗也蕭何”,它的優點也是它的缺點,因為Stop-the-world需要停止我們自己的應用程式,而一些應用需要快速做出響應,而Stop-the-world會導緻應用停止,這并不是我們所期望的。相應的,由于Concurrent是并發地處理垃圾收集任務和應用,是以Concurrent的停頓時間要短,但是由于在垃圾收集的同時,應用還向堆中加入對象,這就好比自己在打掃屋子的同時,别人還向裡面扔垃圾,真的是很煩人啊! 這會增加額外的開銷并且會需要更大的堆記憶體。

Compacting versus Non-compacting versus Copying

當垃圾收集過後,有可能會出現不連續的記憶體空間。Compacting的做法就是把所有活着的對象放到一起,之後就可以用簡單并且快速的指針碰撞來配置設定對象了。由于它需要移到活着的對象,這會導緻垃圾收集過程需要更多的時間。

non-compacting的做法正好與Compacting相反,它在垃圾收集完畢以後并不會把活着的對象放到一起。這樣做的好處就是它更快地完成垃圾收集,但是缺點就會導緻潛在的fragmentation,那麼下次向堆中配置設定對象時,會需要更長的時間,因為它需要搜尋堆記憶體進而找到一塊連續的記憶體足以容納新配置設定的對象。

Copying的做法是把活着的對象複制到一個新的記憶體區域,這樣做的好處就是由于這塊新的區域是空的,我可以從頭到尾快速的配置設定對象并不會産生fragmentation現象。但是缺點也很明顯,就是它需要額外的複制時間和額外的記憶體空間。

性能名額

評價一道美食需要看它的色、香、味,有了這套名額,我們就可以綜合評價這道食物是好是壞。同樣地,對于垃圾收集器來說,也有一套名額來評價其性能,進而判斷一個垃圾收集器是好是壞,本節将會介紹這套名額及其對應的意義。

  • Throughput - 在一段很長的時間内,這段時間沒有花在垃圾收集上的時間的百分比
  • Garbage collection overhead - 與Throughput相反,花費在垃圾收集上的時間的百分比
  • Pause time - 當垃圾收集時,我們自己的應用被停止的時間
  • Frequency of collection - 垃圾收集多久出現一次
  • Footprint - 大小的度量,例如堆大小
  • Promptness - 一個對象變成垃圾到它的這塊記憶體被釋放所需要的時間

垃圾收集類型

由于Hotspot虛拟機的垃圾收集是基于分代思想的,那麼在不同的分代區域收集會産生不同的垃圾收集類型,本節我将會介紹這些垃圾收集類型以及它們發生的時機。

minor gc

發生在年輕代的垃圾收集叫做minor gc,它具體的細節是什麼樣呢?

  • 當JVM不能為一個新對象配置設定空間時,minor gc被觸發。例如:Eden區域被填滿時。是以,你的應用配置設定對象的頻率越高,minor gc發生的越頻繁。
  • 在minor gc期間,老年代實際上被忽略。是以,從老年代到年輕代的引用被當作GC roots,而從年輕代到老年代的引用在标記階段被忽略。
  • minor gc會觸發stop-the-world的發生,緻使應用線程停止。如果在Eden區域中的大部分對象都被标記為垃圾,既符合上面的假設,那麼停頓時間是可以忽略不計的。但是,如果與假設相反,在Eden區域依然大部分的對象都是活着的,那麼minor gc會花費很多的時間。

full gc

清理整個堆的過程叫做full gc,有時也叫做major collection. 當老年代太滿了而不能要接受所有來自年輕代晉升的對象時,所有的收集器(除了CMS)将停止年輕代的收集算法運作,而是用老年代的收集算法清理整個堆記憶體。(CMS垃圾收集器的老年代收集算法不能收集年輕代)。

Hotspot虛拟機中的垃圾收集器

上面我已經介紹了不同的垃圾收集算法以及執行的過程。本節我将介紹Hotspot虛拟機中的垃圾收集器和各個收集器中所使用的收集算法,以及主導垃圾收集的過程。

serial collector

對于serial collector來說,年輕代和老年代的收集都是serial的,并且會導緻Stop-the-world的出現。

serial collector之年輕代收集

這個收集器在年輕代的收集用mark-copy,也就是我上面說的在2個survivor區域之間來回複制。mark階段就是标記出所有活着的對象,copy階段就是把這些活着的對象copy到空的那個survivor區域。注意:如果空的這個survivor區域已經被來自Eden區域和另一個survivor區域中的對象填滿了,那麼剩下活着的對象将被晉升到老年代,不管這些對象已經在幾次minor gc中生存下來。

serial collector之老年代收集

這個收集器在老年代的收集用mark-sweep-compact. 在mark階段,收集器标記出還存活的對象; 在sweep階段,清理掉所有的沒被标記的垃圾; 在compact階段,收集器移動所有活着的對象到老年代的起始端,是以以後在向老年代配置設定對象時就可以用快速且有效的指針碰撞技術了。下圖是compact階段前後的樣子。紅X的對象已死。

{% asset_img 6th.png Compaction of the old generation %}

什麼時候使用serial collector

用-XX:+UseSerialGC參數來啟用serial collector

serial collector用單個線程去執行所有的垃圾收集,由于沒有了線程之間的交流開銷,這使得它稍微高效一些。它最适合在單個處理器的機器上工作,因為它不能利用多處理器硬體的優勢。對于堆空間很小的應用(大約100M),即使在多處理器的硬體上,它也是可行的。

Parallel Collector

Parallel Collector也被叫做throughput collector,對于Parallel Collector來說,年輕代和老年代的收集都是Parallel的,用多個線程去執行收集任務,這會大大減少垃圾收集時間。和serial collector一樣,年輕代和老年代的收集都會導緻Stop-the-world的出現。

Parallel Collector之年輕代收集

和serial collector一樣,它在收集年輕代時,用的也是mark-copy,隻不過是用多個CPU并行而已。相比于serial collector,它确實利用了現代機器多處理器的優勢,大大減少了垃圾收集時的Stop-the-world的時間。由于Parallel Collector在以下2點有效地利用了系統資源進而提高了Throughput.

  1. 在垃圾收集期間,所有的CPU都在執行垃圾收集任務,減小了Stop-the-world的時間
  2. 在各個垃圾收集循環之間(即應用運作的時候),它并沒有像CMS一樣去占用系統的資源

下圖解釋了serial collector和Parallel Collector在年輕代收集之間的不同。

{% asset_img 7th.png Comparison between serial and parallel young generation collection %}

Parallel Collector之老年代收集

它和serial collector一樣,用mark-sweep-compact算法來進行老年代的收集。注意:Parallel Collector在老年代的收集并不是并行的。

雖然老年的收集是不頻繁的,但是一旦它被觸發,就有可能出現很長的Stop-the-world時間,這在一些要求低延遲的應用是不可接受的。如果在你的應用場合需要更低的延遲,那麼相比于Parallel Collector,CMS可能是個很好的選擇。

什麼時候使用Parallel Collector

用-XX:+UseParallelGC參數來啟用Parallel Collector

如果你有一個多處理器的機器,并且你的應用并不需要停頓時間限制,那麼Parallel Collector是個不錯的選擇。

Parallel Compacting Collector

Parallel Compacting Collector幾乎和Parallel Collector一樣,除了Parallel Compacting Collector用一個新的算法針對老年代的收集。Sun公司官方說:Parallel Compacting Collector将最終取代Parallel Collector.

Parallel Compacting Collector之年輕代收集

和parallel collector一樣。

Parallel Compacting Collector之老年代收集

與Parallel Collector不同的是,Parallel Compacting Collector可以并行地對老年代進行收集。

什麼時候使用Parallel Compacting Collector

用-XX:+UseParallelOldGC參數來啟用Parallel Compacting Collector

它和Parallel Collector的使用場景一樣。但是,對于有停頓時間限制的應用,Parallel Compacting Collector會更加合适。

Concurrent Mark-Sweep (CMS) Collector

CMS Collector也叫做low-latency collector,它是專門為老年代設計的。因為年輕代的stop-the-world時間不會太長,而對于老年代來說,雖然它的收集并不頻繁,但是每次收集都可能會出現較長的停頓時間,尤其是在堆空間很大的時候。而CMS Collector的出世就是解決老年代停頓時間長的問題。解決這個問題它主要通過下面2個手段:

  1. 當老年代收集過後,CMS Collector并不會去compacting老年代,而是用空閑清單(free-lists)去管理被釋放的空間。
  2. 它在mark-and-sweep階段大部分的時候都是與我們自己的應用并發執行。

如果你應用的主要目标就是降低延遲,那麼CMS Collector是個非常不錯的選擇。注意:CMS Collector是靠着與我們的應用程式并行才減少了老年代的停頓時間,由于我們的應用一直在運作,是以老年代中的對象也在動态地變化着,是以CMS Collector需要更加小心地進行垃圾收集,這無疑會增加開銷。用stop-the-world的方法收集老年代時,雖然它的停頓時間會長一些,但是它一下就清理幹淨了,然後繼續我們的應用程式。而CMS Collector給我的感覺有點像“細水長流”,它雖然減少了停頓時間,但是它基本上會一直和我們的應用程式一起消耗着CPU資源,相比于Parallel Collector,CMS Collector會有更低的throughput

CMS Collector之年輕代收集

和parallel collector一樣。

CMS Collector之老年代收集

CMS Collector在收集老年代的時候總共需要4個階段,其中有2個階段依然會出現stop-the-world的現象。下圖是CMS Collector與serial collector之間在老年代收集之間的比較,大家先仔細觀察一下,看看有什麼不同。之後我會詳細介紹這4個階段的細節并介紹在每個階段會出現的問題。

{% asset_img 8th.png Comparison between serial and CMS old generation collection %}

  1. initial mark - 會出現stop-the-world
    • 标記出應用代碼直接可達的活着的對象,這個過程需要很短的時間,是以stop-the-world的時間基本可以忽略。initial mark和remark都會出現stop-the-world的現象,但是從上圖我們可以看出,remark用的是多線程并行标記,而initial mark卻用單線程去标記,這是為什麼呢?其實很簡單,如果你用并發去做,就有些“殺雞用宰牛刀”的意味。說直白一點,即使是你用單線程去做這個任務,它依然隻有很短時間,可以忽略不計; 相反,如果你用多線程去做,你需要為各個任務去配置設定線程,線程之間還需要通信開銷,你提前做了這麼多的工作最後卻幹了一件“雞毛蒜皮的小事”,這甚至有可能會導緻你的标記時間相比于單線程來說要增加,是以這裡用單線程去做。
  2. concurrent mark - 與我們的應用程式并發執行
    • 由于在initial mark階段已經找到了GC Roots,那麼在這個階段會從GC Roots出發,沿着引用鍊去找剩下存活的對象。1、由于我們的程式在這個階段下也一直在運作,是以有可能會繼續向老年代中添加對象; 2、雖然在remark階段之後,CMS Collector會保證所有活着的對象被标記,但是在這個過程中一些被認為活着的對象可能已經死了,是以隻能等到下一次老年代的收集才能被回收。由于這2點原因,CMS Collector無疑會比其它的收集器需要更大的堆空間。
  3. remark - 會出現stop-the-world
    • 由于在concurrent mark階段期間,我們自己的應用程式一直在運作,它會更新一些對象的引用,是以在concurrent mark結束後并不能保證所有活着的對象被标記,那麼這個階段就是來解決這個問題的。在這個階段中,它會重新通路所有在concurrent mark期間被改動的對象,保證讓所有活着的對象被标記到。由于這個階段的任務是頭“牛”,是以就如上圖所示,這個階段是用多線程(宰牛刀)去執行的。
  4. concurrent sweep - 與我們的應用程式并發執行
    • 這個階段的任務就是釋放掉垃圾占用的空間。CMS Collector為了減小停頓時間,它在這個階段并沒有進行compacting,而是用空閑清單去維護被釋放的空間。相比于指針碰撞,它在配置設定對象的時候需要更多的時間。在大多數情況下(有一些大對象直接被配置設定到老年代),對象配置設定到老年代是因為年輕代的對象晉升了。由于你用空閑清單的方式去配置設定記憶體會需要更多的時間,是以也會增加年輕代收集的開銷。

什麼時候使用CMS Collector

用-XX:+UseConcMarkSweepGC參數來啟用CMS Collector

相比于parallel collector,CMS Collector減少了老年代的停止時間,但是同時,它也作出了一些犧牲,比如:增加了年輕代的停止時間、減少了throughput和需要更大的堆空間。

如果你的應用需要更短的垃圾收集停頓時間,那麼CMS是個不錯的選擇。

總結

至此,我已經介紹完了Hotspot虛拟機中的垃圾收集算法和垃圾收集器。這裡面沒有哪個收集器可以吃遍天下的,選擇哪個收集器要取決于你的應用,你手中現有的硬體資源。調節垃圾收集器的參數就像我們實際開發中選擇哪個算法也樣,我們結合自己的應用,在時間和空間上要找到一個适合我們自己的tradeoff.

大家在調節GC的時候,千萬不要憑借自己的感覺随便選擇一個GC相關的參數。而是應該遵循下面這幾個簡單的步驟,進而使你在優化GC的道路上進入一個正确的方向。

  1. 明确你的性能目标。Oracle官方的建議就是在開始的時候do nothing,讓GC自己去動态地調節,如果不能滿足你的性能目标的時候再去調節GC. 性能名額分為3類,Latency、Throughput、capacity
  2. 用你目前的GC參數去做測試,記錄下你的性能結果
  3. 把你的測試結果與你自己定義的目标做比較
  4. 如果沒有達到你定的目标,調節相應參數,回到步驟2

過一段時間我會專門寫一篇關于如何調節GC參數的文章并用具體的執行個體來示範整個過程。

還有一點就是:上面的垃圾收集器中我并沒有提到Garbage-First(G1) Garbage Collector,Oracle官方的計劃就是讓G1收集器完全取代CMS Collector. 在2009年的JavaOne大會上,Sun公司釋出了Java SE 6 Update 14,在這個JDK的版本中包括了萬衆矚目的G1收集器。G1是一個low-pause、low-latency的一款收集器,你可以給它設定一個停止時間,它會盡量去滿足你的這個時間。注意:它不能保證達到這個目标。 如果你的CMS收集器目前沒有問題,你完全沒有理由用這個收集器。這裡,我給大家一篇文章,它詳細地描述了G1收集器垃圾收集的細節,G1: Java’s Garbage First Garbage Collector,我就不翻譯了。