垃圾收集器是垃圾回收算法(標記-清除算法、復制算法、標記-整理算法、火車算法)的具體實現,不同商家、不同版本的JVM所提供的垃圾收集器可能會有很在差別,本文主要介紹HotSpot虛擬機中的垃圾收集器。
下圖是java8 HotSpot虛擬機所有的垃圾收集器,連接先代表可也配合使用的組合,G1是對整個堆進行收集
用於新生代的收集器有:Serial、ParNew、Paraller Scavenge
用於老年代的收集器有:CMS(Concurrent Mark Sweep)、Serial Old、Parallel Old
收集器的分類
- 串行垃圾收集器:Serial、Serial Old
- 並行垃圾收集器:ParNew、Parallel Old、Paraller Scavenge
- 並發垃圾收集器:CMS(Concurrent Mark Sweep)
- G1:G1的話比較特殊一點,接下來進行介紹
如何查看當前java默認使用的是哪種垃圾收集器?
java -XX:+PrintCommandLineFlags -version
UseParallelGC代表新生代默認使用的是Paraller Scavenge進行垃圾收集的,而老年代則使用是Parallel Old進行垃圾收集的
當然也可以使用IDEA進行測試,准備測試代碼,目的是查看GC收集的信息
import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; public class GCTest { public static void main(String[] args) throws InterruptedException { List<Object> list = new ArrayList<Object>(); while(true) { int sleep = new Random().nextInt(100); if (System.currentTimeMillis() % 2 == 0) { list.clear(); } else { for (int i = 0; i < 10000; i++) { Properties properties = new Properties(); properties.put("key_" + i, "value_" + System.currentTimeMillis() + i); list.add(properties); } } Thread.sleep(sleep); } } }
在程序啟動之前設置VM options屬性
-XX:+PrintGCDetails -Xms16m -Xmx16m
啟動測試,觀察控制台
新生代和老年的所使用的垃圾收集器如上圖
串行垃圾收集器
-XX:+UseSerialGC -XX:+PrintGCDetails -Xms16m -Xmx16m
控制台打印信息
可以看到垃圾收集已經變為DefNew和Tenured的的組合進行了,也就是Serial和Serial Old的組合
並行垃圾收集器
也就是JAVA8中默認的垃圾收集器,但是還有一個新生代的選擇,那就是Par New,
ParNew 和Parallel Scavenge的不同之處
Parallel Scavenge收集器的特點是它的關注點與其他收集器不同,CMS等收集器的關注點是盡可能地縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量(Throughput)。所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 /(運行用戶代碼時間 + 垃圾收集時間),虛擬機總共運行了100分鍾,其中垃圾收集花掉1分鍾,那吞吐量就是99%。
設置為ParNew進行新生代的垃圾回收
-XX:+UseParNewGC -XX:+PrintGCDetails -Xms16m -Xmx16m
並發垃圾收集器
CMS垃圾收集器

- 初始化標記(CMS-initial-mark) ,標記root,會導致stw;
- 並發標記(CMS-concurrent-mark),與用戶線程同時運行;
- 預清理(CMS-concurrent-preclean),與用戶線程同時運行;
- 重新標記(CMS-remark) ,會導致stw;
- 並發清除(CMS-concurrent-sweep),與用戶線程同時運行;
- 調整堆大小,設置CMS在清理之后進行內存壓縮,目的是清理內存中的碎片;
- 並發重置狀態等待下次CMS的觸發(CMS-concurrent-reset),與用戶線程同時運行;
設置以CMS方式進行垃圾收集
-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xms16m -Xmx16m
G1垃圾收集器
G1垃圾收集器是在jdk1.7中正式使用的全新的垃圾收集器,oracle官方計划在jdk9中將G1變成默認的垃圾收集器,以替代CMS。
原理

- 如果一個對象占用的空間超過了分區容量50%以上,G1收集器就認為這是一個巨型對象。
- 這些巨型對象,默認直接會被分配在老年代,但是如果它是一個短期存在的巨型對象,就會對垃圾收集器造成負面影響。
- 為了解決這個問題,G1划分了一個Humongous區,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那么G1會尋找連續的H分區來存儲。為了能找到連續的H區,有時候不得不啟動Full GC。
Young GC
- Eden空間的數據移動到Survivor空間中,如果Survivor空間不夠,Eden空間的部分數據會直接晉升到年老代空間。
- Survivor區的數據移動到新的Survivor區中,也有部分數據晉升到老年代空間中。最終Eden空間的數據為空,GC停止工作,應用線程繼續執行。
Rembered Set

Mixed GC
-XX:+UseG1GC -XX:+PrintGCDetails -Xms16m -Xmx16m
young GC
Mixed GC
G1的特點
(1)並行與並發
能充分利用多CPU、多核環境下的硬件優勢;可以並行來縮短"Stop The World"停頓時間;也可以並發讓垃圾收集與用戶程序同時進行;
(2)分代收集,收集范圍包括新生代和老年代
能獨立管理整個GC堆(新生代和老年代),而不需要與其他收集器搭配;能夠采用不同方式處理不同時期的對象; 雖然保留分代概念,但Java堆的內存布局有很大差別;將整個堆划分為多個大小相等的獨立區域(Region);新生代和老年代不再是物理隔離,它們都是一部分Region(不需要連續)的集合;