垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法、火车算法)的具体实现,不同商家、不同版本的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(不需要连续)的集合;