文章目錄
導論
7個經典收集器與垃圾分代之間的關係圖
垃圾收集器的組合關係最新圖(jdk14之前),(虛線是jdk8不包括jdk8之前的關係,實線是jdk8之後的關係)
Serial回收器:串行回收
- Serial收集器是最基本、歷史最悠久的垃圾收集器了。JDK1.3之前回收新生代唯一的選擇。
- Serial收集器作爲HotSpot中Client模式下的默認新生代垃圾收集器。
Serial 收集器採用複製算法、串行回收和"Stop-the-World"機制的方式執行內存回收
。- 除了年輕代之外,Serial收集器還提供用於執行老年代垃圾收集的Serial 0ld收集器。
Serial old收集器同樣也採用了串行回收和"Stop the World"機制, 只不過內存回收算法使用的是標記-壓縮算法。
➢Serial old是運行在Client模式下默認的老年代的垃圾回收器
➢Serial old在Server模式下主要有兩個用途:① 與新生代的Parallel Scavenge配合使用 ② 作爲老年代CMS收集器的後備垃圾收集方案。
這個收集器是一個單線程的收集器,但它的“單線程”的意義並不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束(Stop The World).。
優勢:簡單而高效(與其他收集器的單線程比),對於限定單個CPU的環境來說,Serial收集器由於沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。
總結:這種垃圾收集器瞭解
,現在已經不用串行的了。而且在限定單核cpu纔可以用。現在都不是單核的了。對於交互較強的應用而言,這種垃圾收集器是不能接受的。一般在Java web應用程序中是不會採用串行垃圾收集器的。
ParNew回收器:並行回收
-
如果說Serial GC是年輕代中的單線程垃圾收集器,那麼ParNew收集器則是Serial收集器的多線程版本。
➢ Par是Parallel的縮寫,New: 只能處理的是新生代 -
ParNew收集器除了採用
並行回收
的方式執行內存回收外,兩款垃圾收集器之間幾乎沒有任何區別。ParNew收集器在年輕代中同樣也是採用複製算法
、"Stop-the-World"機制。
-
ParNew是很多JVM運行在Server模式下新生代的默認垃圾收集器。
-
對於新生代,回收次數頻繁,使用並行方式高效。
-
對於老年代,回收次數少,使用串行方式節省資源。(CPU並行 需要切換線程,串行可以省去切換線程的資源)
-
由於ParNew收集器是基於並行回收,那麼是否可以斷定ParNew收集器的回收效率在任何場景下都會比serial收集器更高效?
➢ ParNew收集器運行在多CPU的環境下,由於可以充分利用多CPU、多核心等物理硬件資源優勢,可以便快速地完成垃圾收集,提升程序的吞吐量。
➢ 但是在單個CPU的環境下,ParNew收 集器不比serial收集器更高效
。雖然Serial收集器是基於串行回收,但是由於CPU不需要頻繁地做任務切換,因此可以有效避免多線程交互過程中產生的一些額外開銷。 -
因爲除Serial外,目前只有ParNew GC能與CMS收集器配合工作
Parallel Scavenge回收器:吞吐量優先
- Hotspot的年輕代中除了擁有ParNew收集器是基於並行回收的以外,Parallel Scavenge收集器
同樣也採用了複製算法、並行回收和"stop the world"機制
。 - 那麼Paralle1收集器的出現是否多此一舉?
➢ 和ParNew收集器不同,Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量(Throughput)
,它也被稱爲吞吐量優先的垃圾收集器
。
➢ 自適應調節策略也是Parallel Scavenge 與ParNew一個重要區別。 - 高吞吐量則可以高效率地利用CPU時間,儘快完成程序的運算任務,主要
適合在後臺運算而不需要太多交互的任務
。因此,常見在服務器環境中使用。例如,那些執行批量處理、訂單處理、工資支付、科學計算的應用程序。 - Parallel收集器在JDK1.6時提供了用於執行老年代垃圾收集的Parallel 0ld收集器,用來代替老年代的Serial 0ld收集器。
- Parallel 0ld收集器採用了
標記-壓縮算法
,但同樣也是基於並行回收和”stop-the-World"機制。
- 在程序吞吐量優先的應用場景中, Parallel收集器和Parallel old收集器的組合,在Server模式下的內存回收性能很不錯。
在Java8中, 默認是此垃圾收集器。
CMS回收器:低延遲
- 在JDK 1.5時期,HotSpot 推出了一款在
強交互
應用中幾乎可認爲有劃時代意義的垃圾收集器:CMS (Concurrent-Mark- Sweep)
收集器,這款收集器是HotSpot虛擬機中第一款真正意義 上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程同時工作。
- CMS收集器的關注點是儘可能縮短垃圾收集時用戶線程的停頓時間。停頓時
間越短(低延遲)就越適合與用戶交互的程序,良好的響應速度能提升用戶
體驗。
➢目前很大一部分的Java應用集中在互聯網站或者B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗CMS收集器就非常符合這類應用的需求。
- CMS的垃圾收集算法採用
標記-清除
算法,並且也會"Stop-the-world"
不幸的是,CMS作爲老年代的收集器,卻無法與JDK 1.4.0中已經存在的新生代收集器Parallel Scavenge 配合工作,所以在JDK 1. 5中使用CMS來收集老年代的時候,新生代只能選擇ParNew或者Serial收集器中的一個。
在G1出現之前,CMS使用還是非常廣泛的。一直到今天,仍然有很多系統使用CMS GC。
CMS整個過程比之前的收集器要複雜,整個過程分爲4個主要階段
,即初始標記階段、併發標記階段、重新標記階段和併發清除階段
。
- 初始標記(Initial-Mark) 階段:在這個階段中,程序中所有的工作線程都將會因爲“Stop-the-World"機制而出現短暫的暫停,這個階段的主要任務
僅僅只是標記出GCRoots能直接關聯到的對象
。一標記完成之後就會恢復之前被暫停的所有應用線程。由於直接關聯對象比較小,所以這裏的速度非常快。
- 併發標記(Concurrent -Mark)階段:從GC Roots的
直接關聯對象開始遍歷整個對象圖的過程,這個過程耗時較長但是不需要停頓用戶線程,
可以與垃圾收集線程一起併發運行。 - 重新標記(Remark) 階段:由於在併發標記階段中,程序的工作線程會和垃圾收集線程同時運行或者交叉運行,
因此爲了修正併發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,
這個階段的停頓時間通常會比初始標記階段稍長一-些,但也遠比並發標記階段的時間短。 - 併發清除(Concurrent-Sweep) 階段:此階段
清理刪除掉標記階段判斷的已經死亡的對象,釋放內存空間
。由於不需要移動存活對象,所以這個階段也是可以與用戶線程同時併發的.。
儘管CMS收集器採用的是併發回收(非獨佔式),但是在其初始化標記和再次標記這兩個階段中仍然需要執行“Stop-the -World機制
暫停程序中的工作線程,不過暫停時間並不會太長,因此可以說明目前所有的垃圾收集器都做不到完全不需要“Stop-the-World”,只是儘可能地縮短暫停時間。
由於最耗費時間的併發標記與併發清除階段都不需要暫停工作,所以整體的回收是低停頓的。
另外,由於在垃圾收集階段用戶線程沒有中斷,所以在CMS回收過程中,還應該確保應用程序用戶線程有足夠的內存可用
。因此,CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,而是當堆內存使用率達到某一閾值時, 便開始進行回收
,以確保應用程序在CMS工作過程中依然有足夠的空間支持應用程序運行。要是CMS運行期間預留的內存無法滿足程序需要,就會出現一次“Concurrent Mode Failure
'失敗,這時虛擬機將啓動後備預案:臨時啓用Serial 0ld收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。
CMS收集器的垃圾收集算法採用的是標記-清除
算法,這意味着每次執行完內存回收後,由於被執行內存回收的無用對象所佔用的內存空間極有可能是不連續的一些內存塊,不可避免地將會產生一些內存碎片
。那麼CMS在爲新對象分配內存空間時,將無法使用指針碰撞(Bump the Pointer) 技術,而只能夠選擇空閒列表(Free List) 執行內存分配。
- CMS的優點:
➢併發收集
➢低延遲 - CMS的弊端:
會產生內存碎片
,導致併發清除後,用戶線程可用的空間不足。在無法分配大對象的情況下,不得不提前觸發Full GC。CMS收集器對CPU資源非常敏感。
在併發階段,它雖然不會導致用戶停頓,但是會因爲佔用了一部分線程而導致應用程序變慢,總吞吐量會降低。- CMS收集器無法處理浮動垃圾。可能出現“Concurrent Mode Fa ilure"失敗而導致另一次Full GC的產生。在併發標記階段由於程序的工作線程和垃圾收集線程是同時運行或者交叉運行的,
那麼在併發標記階段如果產生新的垃圾對象,CMS將無法對這些垃圾對象進行標記,最終會導致這些新產生的垃圾對象沒有被及時回收,
從而只能在下一次執行GC時釋放這些之前未被回收的內存空間。
小結:
HotSpot有這麼多的垃圾回收器,那麼如果有人問,Serial GC、Parallel GC、Concurrent Mark Sweep GC這三個GC有什麼不同呢?
如果你想要最小化地使用內存和並行開銷,請選Serial GC;
如果你想要最大化應用程序的吞吐量,請選Parallel GC;
如果你想要最小化GC的中斷或停頓時間,請選CMS GC。
G1回收器:區域化分代式
既然我們已經有了前面幾個強大的GC,爲什麼還要發佈Garbage First (G1)
GC?
原因就在於應用程序所應對的業務越來越龐大、複雜,用戶越來越多,沒有GC就不能保證應用程序正常進行,而經常造成STW的GC又跟不上實際的需求,所以纔會不斷地嘗試對GC進行優化。G1 (Garbage-First)垃圾回收器是在Java7 update 4之後引入的一個新的垃圾回收器,是當今收集器技術發展的最前沿成果之一。
與此同時,爲了適應現在不斷擴大的內存和不斷增加的處理器數量
,進一步降低暫停時間(pause time),同時兼顧良好的吞吐量。
官方給G1設定的目標是在延遲可控的情況下獲得儘可能高的吞吐量,所以才擔 當起“全功能收集器”的重任與期望。
爲什麼名字叫做Garbage First (G1) 呢?
- 因爲G1是一個並行回收器,它把堆內存分割爲很多不相關的區域(Region) (物理上不連續的)。使用不同的Region來表示Eden、倖存者0區,倖存者1區,老年代等。
- G1 GC有計劃地避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個Region裏面的垃圾堆積的價值大小( 回收所獲得的空間大小以及回收所需時間的經驗值),在後臺維護-一個優先列表,
每次根據允許的收集時間,優先回收價值最大的Region.
- 由於這種方式的側重點在於回收垃圾最大量的區間(Region) ,所以我們給G1一個名字:垃圾優先(Garbage First)
G1 (Garbage-First) 是一款面向服務端應用的垃圾收集器,主要針對配備多核CPU及大容量內存的機器
,以極高概率滿足GC停頓時間的同時,還兼具高吞吐量的性能特徵。
在JDK1.7版本正式啓用,移除了Experimental的標識,是JDK 9以後的默認垃圾回收器
,取代了CMS回收器以及Parallel + Parallel 0ld組合。被Oracle官方稱爲“全功能的垃圾收集器”
與此同時,CMS已經在JDK 9中被標記爲廢棄(deprecated)。在jdk8中還不是默認的垃圾回收器,需要使用-XX: +UseG1GC來啓用。
特點
- 並行與併發
➢ 並行性: G1在回收期間,可以有多個Gc線程同時工作,有效利用多核計算能力。此時用戶線程sTW
➢ 併發性: G1擁有與應用程序交替執行的能力,部分工作可以和應用程序同時執行,因此,一般來說,不會在整個回收階段發生完全阻塞應用程序的情況 - 分代收集
➢ 從分代上看,G1依然屬於分代型垃圾回收器
,它會區分年輕代和老年代,年輕代依然有Eden區和Survivor區。但從堆的結構上看,它不要求整個Eden區、年輕代或者老年代都是連續的,也不再堅持固定大小和固定數量。
➢ 將堆空間分爲若干個區域(Region) ,這些區域中包含了邏輯上的年輕代和老年代。
➢ 和之前的各類回收器不同,它同時兼顧年輕代和老年代
。對比其他回收器,或者工作在年輕代,或者工作在老年代 - 可預測的停頓時間模型(即:軟實時soft real-time)
這是G1相對於CMS的另一大優勢,G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在-一個長度爲M亳秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
➢ 由於分區的原因,G1可以只選取部分區域進行內存回收,這樣縮小了回收的範圍,因此對於全局停頓情況的發生也能得到較好的控制。
➢ G1跟蹤各個Region裏面的垃圾堆積的價值大小(回收所獲得的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的Region
,保證了G1 收集器在有限的時間內可以獲取儘可能高的收集效率。
➢ 相比於CMS GC, G1未必能做到cMS在最好情況下的延時停頓,但是最差情況要好很多。 - 空間整合
➢ CMS:“標記-清除”算法、內存碎片、若干次GC後進行- -次碎片整理
➢ G1將內存劃分爲-一個個的region。內存的回收是以region作爲基本單位的。Region之間是複製算法
,但整體上實際可看作是標記-壓縮(Mark-Compact )算法
,兩種算法都可以避免內存碎片。這種特性有利於程序長時間運行,分配大對象時不會因爲無法找到連續內存空間而提前觸發下一次GC。尤其是當Java堆非常大的時候,G1的優勢更加明顯。
相較於CMS,G1還不具備全方位、壓倒性優勢。比如在用戶程序運行過程中,G1無論是爲了垃圾收集產生的內存佔用(Footprint)還是程序運行時的額外執行負載(Overload) 都要比CMS要高。
從經驗.上來說,在小內存應用上CMS的表現大概率會優於G1,而G1在大內存應用上則發揮其優勢。平衡點在6- 8GB之間。
使用場景
- 面向服務端應用,針對具有大內存、多處理器的機器。(在普通大小的堆裏表現並不驚喜)
- 最主要的應用是需要低GC延遲,並具有大堆的應用程序提供解決方案;
- 如:在堆大小約6GB或更大時,可預測的暫停時間可以低於0.5秒; ( G1通過每次只清理一部分而不是全部的Region的增量式清理來保證每次GC停頓時間不會過長)。
- 用來替換掉JDK1.5中的CMS收集器;在下面的情況時,使用G1可能比CMS好:①超過50%的Java堆被活動數據佔用;;②對象分配頻率或年代提升頻率變化很大;③GC停頓時間過長(長於0. 5至1秒)。
- HotSpot 垃圾收集器裏,除了G1以外,其他的垃圾收集器使用內置的JVM線程執行GC的多線程操作,而61 GC可以採用應用線程承擔後臺運行的GC工作,即當JVM的GC線程處理速度慢時,系統會調用應用程序線程幫助加速垃圾回收過程。
分區Region:化整爲零
使用G1收集器時,它將整個Java堆劃分成約2048個大小相同的獨立Region塊,每個Region塊大小根據堆空間的實際大小而定,整體被控制在1MB到32MB之間,且爲2的N次冪, 即1MB, 2MB, 4MB, 8MB, 16MB, 32MB。可以通過-XX:G1HeapRegionSize設定。所有的Region大小相同,且 在JVM生命週期內不會被改變。
雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Region (不需要連續)的集合。通過Region的動態分配方式實現邏輯.上的連續。
設置H的原因:對於堆中的大對象,默認直接會被分配到老年代,但是如果它是一個短期存在的大對象,就會對垃圾收集器造成負面影響。爲了解決這個問題,G1劃分了一個Humongous區,它用來專門存放大對象。如果一個H區裝不下一個大對象,那麼G1會尋找連續的H區來存儲
。爲了能找到連續的H區,有時候不得不啓動Full GC。G1的大多數行爲都把H區作爲老年代的一部分來看待。
Remembered Set
- 一個對象被不同區域引用的問題
- 一個Region不可能是孤立的,一個Region中的對象可能被其他任意Region中對象引用,判斷對象存活時,是否需要掃描整個Java堆才能保證準確?
- 在其他的分代收集器,也存在這樣的問題(而G1更突出)
- 回收新生代也不得不同時掃描老年代?
- 這樣的話會降低Minor GC的效率;
解決方法
➢ 無論G1還是其他分代收集器,JVM都是使用Remembered Set來避免全局掃描:
➢ 每個Region 都有一個對應的Remembered Set;
➢ 每次Reference類 型數據寫操作時,都會產生一個Write Barrier暫 時中斷操作;
➢ 然後檢查將要寫入的引用指向的對象是否和該Reference類型數據在不同的Region(其他收集器:檢查老年代對象是否引用了新生代對象) ;
➢如果不同,通過CardTable把相關引用信息記錄到引用指向對象的所在Region對應的Remembered Set中;
➢當進行垃圾收集時,在GC根節點的枚舉範圍加入Remembered Set; 就可以保證不進行全局掃描,也不會有遺漏。
回收過程
G1 GC的垃圾回收過程主要包括如下三個環節:
- 年輕代GC (Young GC)
- 老年代併發標記過程(Concurrent Marking)
- 混合回收(Mixed GC)
- (如果需要,單線程、獨佔式、高強度的Full GC還是繼續存在的。它針對GC的評估失敗提供了一種失敗保護機制,即強力回收。)
應用程序分配內存,當年輕代的Eden區用盡時開始年輕代回收過程
; G1的年輕代收集階段是一個並行的獨佔式收集器
。在年輕代回收期,G1 GC暫停所有應用程序線程,啓動多線程執行年輕代回收。然後從年輕代區間移動存活對象到Survivor區間或者老年區間,也有可能是兩個區間都會涉及。
當堆內存使用達到一定值(默認45%)時,開始老年代併發標記過程。
標記完成馬上開始混合回收過程。對於一個混合回收期,G1 GC從老年區間移動存活對象到空閒區間,這些空閒區間也就成爲了老年代的一部分。和年輕代不同,老年代的G1回收器和其他Gc不同,G1的老年代回收器不需要整個老年代被回收,一次只需要掃描/回收一小部分老年代的Region就可以了。
同時,這個老年代Region是和年輕代一起 被回收的。
舉個例子:一個Web服務器,Java進程最大堆內存爲4G,每分鐘響應1500個請求,每45秒鐘會新分配大約2G的內存。G1 會每45秒鐘進行一次年輕代回收,每31 個小時整個堆的使用率會達到45號,會開始老年代併發標記過程,標記完成後開始四到五次的混合回收。
G1回收過程一:年輕代GC
JVM啓動時,G1先準備好Eden區,程序在運行過程中不斷創建對象到Eden區,當Eden空間耗盡時,G1會啓動- -次年輕代垃圾回收過程。
YGC時,首先G1停止應用程序的執行(Stop-The-World) ,G1創建回收集(Collection Set) ,回收集是指需要被回收的內存分段的集合,年輕代回收過程的回收集包含年輕代Eden區和Survivor區所有的內存分段。
第一階段,掃描根
。
根是指static變量指向的對象,正在執行的方法調用鏈條上的局部變量等。根引用連同RSet記錄的外部引用作爲掃描存活對象的入口。
第二階段,更新RSet.
處理dirty card queue( 見備註)中的card,更新RSet。此階段完成後,RSet可以準確的反映老年代對所在的內存分段中對象的引用。
第三階段,處理RSet.
識別被老年代對象指向的Eden中的對象,這些被指向的Eden中的對象被認爲是存活的對象。
第四階段,複製對象。
此階段,對象樹被遍歷,Eden區內存段中存活的對象會被複制到Survivor區中空的內存分段Survivor區內存段中存活的對象如果年齡未達閾值,年齡會加1,達到閥值會被會被複制到01d區中空的內存分段。如果Survivor空間不夠,Eden空 間的部分數據會直接晉升到老年代空間。
第五階段,處理引用
。
處理Soft,Weak,Phantom, Final, JNI Weak 等引用。最終Eden空間的數據爲空,GC停止工作,而目標內存中的對象都是連續存儲的,沒有碎片,所以複製過程可以達到內存整理的效果,減少碎片。禮工工河辦小學的社
G1回收過程二:併發標記過程
初始標記階段
:標記從根節點直接可達的對象。這個階段是STW的並且會觸發一次
年輕代GC。根區域掃描(Root Region Scanning)
: G1 GC掃描Survivor區直接可達的老年代區域對象,並標記被引用的對象。這一過程必須在young GC之前完成。併發標記(Concurrent Marking)
: 在整個堆中進行併發標記(和應用程序併發執行),此過程可能被young GC中斷。在併發標記階段,若發現區域對象中的所有對象都是垃圾,那這個區域會被立即回收。同時,併發標記過程中,會計算每個區域的對象活性(域中存活對象的比例)。再次標記(Remark
):由 於應用程序持續進行,需要修正上一次的標記結果。是STW的。G1中採用了比CMS更快的初始快照算法: snapshot-at-the-beginning (SATB)。獨佔清理(cleanup ,STW
):計算各個區域的存活對象和GC回收比例,並進行排序,識別可以混合回收的區域。爲下階段做鋪墊。是STW的。
➢這個階段並不會實際上去做垃圾的收集併發清理階段
:識別並清理完全空閒的區域。
G1回收過程三:混合回收
當越來越多的對象晉升到老年代oldregion時,爲了避免堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器即Mixed GC, 該算法並不是一個oldGC,除了回收整個Young Region,還會回收一部分的0ld Region。 這裏需要注意:是一部分老年代,而不是全部老年代
。可以選擇哪些0ldRegion進行收集,從而可以對垃圾回收的耗時時間進行控制。也要注意的是Mixed GC並不是Full GC。
- 併發標記結束以後,老年代中百分百爲垃圾的內存分段被回收了,部分爲垃圾的內存分段被計算了出來。默認情況下,這些老年代的內存分段會分8次(可以通過-XX:G1MixedGCCountTarget設置)被回收。
- 混合回收的回收集( Collection Set)包括八分之一的老年代內存分段,Eden區內存分段,Survivor區內存分段。混合回收的算法和年輕代回收的算法完全-一樣,只回集多了老年代的內存分段。具體過程請參考上面的年輕代回收過程。
- 由於老年代中的內存分段默認分8次回收,G1會優先回收垃圾多的內存分段。垃圾佔內存分段比例越高的,越會被先回收。並且有一個閾值會決定內存分段是否被回收,-Xx: G1Mi xedGCLiveThresholdPercent,默認爲65%,意思是垃圾佔內存分段比例要達到65%纔會被回收。如果垃圾佔比太低,意味着存活的對象佔比高,在複製的時候會花費更多的時間。
- 混合回收並不一定要進行8次。有一個閾值-XX: G1HeapWastePercent,默認值爲10%,意思是允許整個堆內存中有10%的空間被浪費,意味着如果發現可以回收的垃圾佔堆內存的比例低於10%,則不再進行混合回收。因爲GC會花費很多的時間但是回收到的內存卻很少。.
G1回收可選的過程四: Full GC
G1的初衷就是要避免Full GC的出現。但是如果上述方式不能正常工作,G1會停止應用程序的執行(Stop-The -World)
,使用單線程
的內存回收算法進行垃圾回收,性能會非常差,應用程序停頓時間會很長。
要避免Full GC的發生,一旦發生需要進行調整。什麼時候會發生Full GC呢?比如堆內存太小
,當G1在複製存活對象的時候沒有空的內存分段可用,則會回退到full gc, 這種情況可以通過增大內存解決。
導致G1Full GC的原因可能有兩個:1. Evacuation的時候沒有足夠的to-space來存放晉升的對象;2. 併發處理過程完成之前空間耗盡。
總結
截止JDK 1.8,一共有7款不同的垃圾收集器。每一.款不同的垃圾收集器都有不同的特點,在具體使用的時候,需要根據具體的情況選用不同的垃圾收集器。