java中的七大GC收集器和四大GC算法
垃圾收集器的种类和介绍
1、serial 串行垃圾收集器,采用单线程收集垃圾
2、parallel 并行垃圾收集器,采用多线程收集垃圾
3、CMS(concurrentMarkSweep)并发标记收集 垃圾收集器,串行和并行同在,是前两种垃圾收集器的优化,较短时间进行STW(stop the world),保证较快的响应速度。
4、G1 新一代垃圾收集器,采用的region分区 
java8中默认的垃圾收集器
jps -l
jinfo -flags pid
 java8中默认使用的是ParallelGC
java中七大垃圾收集器
年轻代GC
1、UserSerialGC:串行垃圾收集器 2、UserParallelGC:并行垃圾收集器 3、UseParNewGC:年轻代的并行垃圾回收器
新生代中采用的垃圾收集算法 基本都是 标记复制算法/复制拷贝
老年代GC
1、UserSerialOldGC:串行老年代垃圾收集器(已经被移除) 2、UseParallelOldGC:老年代的并行垃圾回收器 3、UseConcMarkSweepGC:(CMS)并发标记清除
老年代中采用的来及收集算法 基本都是 标记清除,标记清除压缩
youngGen 和 oldGen 使用的GC
UseG1GC:G1垃圾收集器
七大垃圾收集器搭配和介绍
下面两个图很重要
注意:下面图中指的是可以这样搭配,但是jvm中会有默认搭配机制


Serial收集器
一个单线程的收集器,在进行垃圾收集时候,必须暂停其他所有的工作线程(STW)直到它收集结束。
新生代老年代都是单线程的垃圾回收
STW: Stop The World
串行收集器是最古老,最稳定以及效率高的收集器,只使用一个线程去回收但其在进行垃圾收集过程中可能会产生较长的停顿(Stop-The-World”状态)。虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。
 如果让jvm使用Serial垃圾收集器
-XX:+UseSerialGC
实例:
jvm配置参数 -XX:+UseSerialGC -XX:+PrintGCDetails -XX:MaxHeapSize=5m -XX:InitialHeapSize=5m
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
while (true){
list.add(new Object());
}
}
-XX:InitialHeapSize=5242880 -XX:MaxHeapSize=5242880 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
[GC (Allocation Failure) [DefNew: 1664K->191K(1856K), 0.0017261 secs] 1664K->730K(5952K), 0.0017851 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1855K->192K(1856K), 0.0029624 secs] 2394K->1366K(5952K), 0.0030089 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1856K->192K(1856K), 0.0028893 secs] 3030K->2443K(5952K), 0.0029367 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 1856K->191K(1856K), 0.0042756 secs] 4107K->4107K(5952K), 0.0043167 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 1855K->1855K(1856K), 0.0000200 secs][Tenured: 3915K->3195K(4096K), 0.0125114 secs] 5771K->4728K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0125823 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 3715K->3715K(4096K), 0.0106739 secs] 5571K->5571K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0107996 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 3715K->3610K(4096K), 0.0125134 secs] 5571K->5466K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0125436 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.xiaoxu.test_error.ErrorTest.main(ErrorTest.java:16)
Heap
def new generation total 1856K, used 1855K [0x00000000ffa00000, 0x00000000ffc00000, 0x00000000ffc00000)
eden space 1664K, 100% used [0x00000000ffa00000, 0x00000000ffba0000, 0x00000000ffba0000)
from space 192K, 99% used [0x00000000ffba0000, 0x00000000ffbcfff8, 0x00000000ffbd0000)
to space 192K, 0% used [0x00000000ffbd0000, 0x00000000ffbd0000, 0x00000000ffc00000)
tenured generation total 4096K, used 3640K [0x00000000ffc00000, 0x0000000100000000, 0x0000000100000000)
the space 4096K, 88% used [0x00000000ffc00000, 0x00000000fff8e108, 0x00000000fff8e200, 0x0000000100000000)
Metaspace used 3362K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
ParNew收集器
paraNew是在新生代中的并行多线程垃圾收集器,老年代采用单线程垃圾收集器。

ParNew(Young区)+ Serial Old的收集器组合,新生代使用复制算法,老年代采用标记-整理算法
jvm参数配置
-XX:+UseParNewGC -XX:+PrintGCDetails -XX:MaxHeapSize=5m -XX:InitialHeapSize=5m
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
while (true){
list.add(new Object());
}
}
[GC (Allocation Failure) [ParNew: 1664K->192K(1856K), 0.0017274 secs] 1664K->774K(5952K), 0.0017788 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1856K->192K(1856K), 0.0018861 secs] 2438K->1443K(5952K), 0.0019318 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1439K->192K(1856K), 0.0022849 secs] 2690K->2497K(5952K), 0.0023308 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1856K->192K(1856K), 0.0029927 secs] 4161K->3778K(5952K), 0.0030483 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 990K->990K(1856K), 0.0000341 secs][Tenured: 3586K->4095K(4096K), 0.0135080 secs] 4576K->4174K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0135917 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 4095K->3399K(4096K), 0.0126730 secs] 5951K->5176K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0127294 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 3732K->3732K(4096K), 0.0117807 secs] 5587K->5587K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0118350 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [Tenured: 3732K->3608K(4096K), 0.0141602 secs] 5587K->5464K(5952K), [Metaspace: 3329K->3329K(1056768K)], 0.0142101 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.xiaoxu.test_error.ErrorTest.main(ErrorTest.java:16)
Heap
par new generation total 1856K, used 1855K [0x00000000ffa00000, 0x00000000ffc00000, 0x00000000ffc00000)
eden space 1664K, 99% used [0x00000000ffa00000, 0x00000000ffb9fff8, 0x00000000ffba0000)
from space 192K, 100% used [0x00000000ffba0000, 0x00000000ffbd0000, 0x00000000ffbd0000)
to space 192K, 0% used [0x00000000ffbd0000, 0x00000000ffbd0000, 0x00000000ffc00000)
tenured generation total 4096K, used 3637K [0x00000000ffc00000, 0x0000000100000000, 0x0000000100000000)
the space 4096K, 88% used [0x00000000ffc00000, 0x00000000fff8d728, 0x00000000fff8d800, 0x0000000100000000)
Metaspace used 3362K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
Parallel/Parallel Scavenge收集器(java8默认的垃圾收集器)
新生代和老年代都采用多线程的垃圾收集器

jvm参数配置 -XX:+UseParallelGC -XX:+PrintGCDetails -XX:MaxHeapSize=5m -XX:InitialHeapSize=5m
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
while (true){
list.add(new Object());
}
}
[Full GC (Ergonomics) [PSYoungGen: 1023K->1023K(1536K)] [ParOldGen: 4026K->4026K(4096K)] 5050K->5050K(5632K), [Metaspace: 3463K->3463K(1056768K)], 0.0296184 secs] [Times: user=0.14 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 1023K->1023K(1536K)] [ParOldGen: 4027K->4027K(4096K)] 5051K->5051K(5632K), [Metaspace: 3463K->3463K(1056768K)], 0.0288021 secs] [Times: user=0.09 sys=0.02, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 1023K->1023K(1536K)] [ParOldGen: 4028K->4028K(4096K)] 5052K->5052K(5632K), [Metaspace: 3463K->3463K(1056768K)], 0.0301605 secs] [Times: user=0.13 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 1023K->0K(1536K)] [ParOldGen: 4036K->880K(4096K)] 5060K->880K(5632K), [Metaspace: 3464K->3464K(1056768K)], 0.0078608 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.xiaoxu.test_error.ErrorTest.main(ErrorTest.java:16)
Heap
PSYoungGen total 1536K, used 127K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 12% used [0x00000000ffe00000,0x00000000ffe1fd78,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 4096K, used 880K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
object space 4096K, 21% used [0x00000000ffa00000,0x00000000ffadc3a0,0x00000000ffe00000)
Metaspace used 3496K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 380K, capacity 388K, committed 512K, reserved 1048576K
ParallelOld收集器
ParallelOld收集器是老年代采用的垃圾收集器,使用的算法是标记清除, 与之对应的新生代垃圾收集器ParallelScavenge
上文介绍过
即使我配置-XX:UseParallelOldGC,新生代默认也会配置成ParallelScavenge 注意这是默认的。新生代配置ParallelScavenge,老年代还是配置成这个
CMS(ConcurrentMarkSweep)收集器(建议使用)
CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。
适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短。
CMS非常适合地内存大、CPU核数多的服务器端应用,也是G1出现之前大型应用的首选收集器。 
注意
新生代使用ParNew收集器,老年代会使用收集器SerialOldGC
使用jvm参数配置 -XX:+UseConcMarkSweepGC 新生代会自动使用ParNew
开启该参数后,使用ParNew(Young区用)+ CMS(Old区用)+ Serial Old的收集器组合,Serial Old将作为CMS出错的后备收集器。
4步过程:
1、初始标记(CMS initial mark) - 只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
2、并发标记(CMS concurrent mark)和用户线程一起 - 进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。主要标记过程,标记全部对象。
3、重新标记(CMS remark)- 为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正。
4、并发清除(CMS concurrent sweep) - 清除GCRoots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理对象,由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。
优点:并发收集低停顿,响应速度快。
缺点:并发执行,对CPU资源压力大,采用的标记清除算法会导致大量碎片。
jvm参数配置 -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:MaxHeapSize=5m -XX:InitialHeapSize=5m
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
while (true){
list.add(new Object());
}
}
[GC (Allocation Failure) [ParNew: 1088K->128K(1216K), 0.0030865 secs] 1088K->621K(6016K), 0.0031883 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1216K->128K(1216K), 0.0011983 secs] 1709K->848K(6016K), 0.0012514 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1216K->128K(1216K), 0.0026329 secs] 1936K->1461K(6016K), 0.0026799 secs] [Times: user=0.06 sys=0.03, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1216K->128K(1216K), 0.0026583 secs] 2549K->2637K(6016K), 0.0026936 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 2509K(4800K)] 2644K(6016K), 0.0002571 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 1216K->128K(1216K), 0.0036117 secs] 3725K->4048K(6016K), 0.0036503 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [ParNew: 1216K->1216K(1216K), 0.0000303 secs][CMS[CMS-concurrent-mark: 0.005/0.011 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
(concurrent mode failure): 3920K->4513K(4800K), 0.0150135 secs] 5136K->4513K(6016K), [Metaspace: 3329K->3329K(1056768K)], 0.0151086 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
[GC (Allocation Failure) [ParNew: 486K->486K(1216K), 0.0000183 secs][CMS: 4513K->4799K(4800K), 0.0167265 secs] 4999K->5050K(6016K), [Metaspace: 3329K->3329K(1056768K)], 0.0168074 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
[Full GC (Allocation Failure) [CMS: 4799K->4799K(4800K), 0.0141188 secs] 5050K->5038K(6016K), [Metaspace: 3329K->3329K(1056768K)], 0.0141803 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 4799K(4800K)] 5038K(6016K), 0.0004316 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.xiaoxu.test_error.ErrorTest.main(ErrorTest.java:16)
Heap
par new generation total 1216K, used 291K [0x00000000ffa00000, 0x00000000ffb50000, 0x00000000ffb50000)
eden space 1088K, 26% used [0x00000000ffa00000, 0x00000000ffa48ef0, 0x00000000ffb10000)
from space 128K, 0% used [0x00000000ffb30000, 0x00000000ffb30000, 0x00000000ffb50000)
to space 128K, 0% used [0x00000000ffb10000, 0x00000000ffb10000, 0x00000000ffb30000)
concurrent mark-sweep generation total 4800K, used 4799K [0x00000000ffb50000, 0x0000000100000000, 0x0000000100000000)
Metaspace used 3362K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
G1垃圾收集器
后面讲
SerailOldGC收集器
单线程,标记整理算法,不再赘述
GC如何选择垃圾收集器
组合的选择
单CPU或者小内存,单机程序 -XX:+UseSerialGC
多CPU,需要最大的吞吐量,如后台计算型应用(java8默认) -XX:+UseParallelGC(这两个相互激活) -XX:+UseParallelOldGC
多CPU,追求低停顿时间,需要快速响应如互联网应用 -XX:+UseConcMarkSweepGC -XX:+ParNewGC
G1收集器
G1 (Garbage-First)收集器,是一款面向服务端应用的收集器
特点:
G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW。
G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域(Region),可以近似理解为一个围棋的棋盘。
G1收集器里面讲整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分Region的集合且不需要Region是连续的,也就是说依然会采用不同的GC方式来处理不同的区域。
G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换。
目的
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色:
G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。
G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。
CMS垃圾收集器虽然减少了暂停应用程序的运行时间,但是它还是存在着内存碎片问题。于是,为了去除内存碎片问题,同时又保留CMS垃圾收集器低暂停时间的优点,JAVA7发布了一个新的垃圾收集器-G1垃圾收集器。
G1是在2012年才在jdk1.7u4中可用。oracle官方计划在JDK9中将G1变成默认的垃圾收集器以替代CMS。它是一款面向服务端应用的收集器,主要应用在多CPU和大内存服务器环境下,极大的减少垃圾收集的停顿时间,全面提升服务器的性能,逐步替换java8以前的CMS收集器。
主要改变是Eden,Survivor和Tenured等内存区域不再是连续的了,而是变成了一个个大小一样的region ,每个region从1M到32M不等。一个region有可能属于Eden,Survivor或者Tenured内存区域。

回收步骤
G1收集器下的Young GC
针对Eden区进行收集,Eden区耗尽后会被触发,主要是小区域收集+形成连续的内存块,避免内存碎片
Eden区的数据移动到Survivor区,假如出现Survivor区空间不够,Eden区数据会部会晋升到Old区。 Survivor区的数据移动到新的Survivor区,部会数据晋升到Old区。 最后Eden区收拾干净了,GC结束,用户的应用程序继续执行。

4步过程:
初始标记:只标记GC Roots能直接关联到的对象 并发标记:进行GC Roots Tracing的过程 最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象 筛选回收:根据时间来进行价值最大化的回收

G1参数配置及和CMS的比较
-XX:+UseG1GC
-XX:G1HeapRegionSize=n:设置的G1区域的大小。值是2的幂,范围是1MB到32MB。目标是根据最小的Java堆大小划分出约2048个区域。
-XX:MaxGCPauseMillis=n:最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间。
-XX:InitiatingHeapOccupancyPercent=n:堆占用了多少的时候就触发GC,默认为45。
-XX:ConcGCThreads=n:并发GC使用的线程数。
-XX:G1ReservePercent=n:设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,默认值是10%。
开发人员仅仅需要声明以下参数即可:
三步归纳:开始G1+设置最大内存+设置最大停顿时间
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100 -XX:MaxGCPauseMillis=n:最大GC停顿时间单位毫秒,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间
G1和CMS比较
G1不会产生内碎片 是可以精准控制停顿。该收集器是把整个堆(新生代、老年代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。
java程序启动,配置jvm参数
java -server jvm的各种参数 -jar jar/war包名
|