JVM中的垃圾收集器
大家好!我是渣渣鑫。接着上一篇关于垃圾回收机制的标志算法,我们继续来聊一聊垃圾回收的主要方法。
目录
总览
新生代收集器
老年代收集器
新生代和老年代
一、总览
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
在新生代工作的垃圾收集器:Serial,ParNew,Parallel Scavenge。
在老年代工作的垃圾回收器:CMS,Serial Old,Parallel Old。
即可以在老年代也可以在新生代工作的垃圾回收器:G1。
图中的连线代表收集器之间可以相互合作。
二、新生代收集器
Serial收集器
该收集器工作在新生代,单线程的垃圾收集器,单线程意味着他只会使用一个CPU或者一个收集线程来完成垃圾收集。
在我们看来,单线程垃圾收集器是不太实用的,但是在不同的应用场景有不同算法的优势。比如在Client模式下(客户端模式),他简单有效,无需与其他线程交互,减少了开销。Serial收集器是新生代默认的收集器。
ParNew收集器
该收集器是Serial收集器的多线程版本,ParNew收集器是工作在Server模式下(高性能计算机)。对于高性能服务端来说,收到的请求就会多,要处理的垃圾回收就多,只有减少STW的时间,才能提升效率。所以ParNew是许多运行在Server模式下虚拟机的首选收集器。另一个原因就是只有PaeNew收集器可以与CMS收集器配合使用。(这里的CMS收集器是跨时代的收集器,是真正意义上的并发收集器,他第一次实现了垃圾收集器与用户线程同时执行)。
在多 CPU 的情况下,由于 ParNew 的多线程回收特性,毫无疑问垃圾收集会更快,也能有效地减少 STW 的时间,提升应用的响应速度。
Parallel Scavenge 收集器
Parallel Scavenge收集器也是一个使用复制算法,工作在多线程下的垃圾收集器,那么他与ParNew收集器有什么区别呢?
关注点不同,CMS 等垃圾收集器关注的是尽可能缩短垃圾收集时用户线程的停顿时间,而 Parallel Scavenge 目标是达到一个可控制的吞吐量(吞吐量 = 运行用户代码时间 / (运行用户代码时间+垃圾收集时间)),也就是说 CMS 等垃圾收集器更适合用到与用户交互的程序,因为停顿时间越短,用户体验越好,而 Parallel Scavenge 收集器关注的是吞吐量,所以更适合做后台运算等不需要太多用户交互的任务。
三、老年代收集器
Serial Old收集器
上面我们学习了Serial收集器,他是工作于年轻代的单线程收集器,那么Serial Old就是工作在老年代的单线程收集器,该收集器主要意义在于给Client模式下的虚拟机使用,如果在Server模式下他有两个用途,在jdk1.5之前,Serial Old与parallel Scavenge配合使用。另一种作用是作为CMS收集器的后备使用。
Parallel Old收集器
Parallel Old收集器是相对于Parallel Scavenge收集器的老年代版本,真正提现了吞吐量优先的原则。
CMS收集器
初始标记
并发标记
重新标记
并发清除
从图中可以的看到初始标记和重新标记两个阶段会发生 STW,造成用户线程挂起,不过初始标记仅标记 GC Roots 能关联的对象,速度很快,并发标记是进行 GC Roots Tracing 的过程,重新标记是为了修正并发标记期间因用户线程继续运行而导致标记产生变动的那一部分对象的标记记录,这一阶段停顿时间一般比初始标记阶段稍长,但远比并发标记时间短。
整个过程中耗时最长的是并发标记和标记清理,不过这两个阶段用户线程都可工作,所以不影响应用的正常使用,所以总体上看,可以认为 CMS 收集器的内存回收过程是与用户线程一起并发执行的。
四、G1(Garbage First)收集器
G1 收集器是面向服务端的垃圾收集器,被称为驾驭一切的垃圾回收器,主要有以下几个特点
-
像 CMS 收集器一样,能与应用程序线程并发执行。 -
整理空闲空间更快。 -
需要 GC 停顿时间更好预测。 -
不会像 CMS 那样牺牲大量的吞吐性能。 -
不需要更大的 Java Heap
-
运作期间不会产生内存碎片,G1 从整体上看采用的是标记-整理法,局部(两个 Region)上看是基于复制算法实现的,两个算法都不会产生内存碎片,收集后提供规整的可用内存,这样有利于程序的长时间运行。 -
在 STW 上建立了可预测的停顿时间模型,用户可以指定期望停顿时间,G1 会将停顿时间控制在用户设定的停顿时间以内。
G1 收集器的工作步骤如下
-
初始标记 -
并发标记 -
最终标记 -
筛选回收