垃圾收集器
![垃圾收集器](https://i-blog.csdnimg.cn/blog_migrate/c5291ee10eafce818ff432bd46da9846.png)
-
Serial收集器
Serial收集器是最基本、历史最悠久的收集器,曾经(JDK1.3.1之前)是虚拟机新生代收集的唯一选择。这个收集器是一个单线程收集器,但它的”单线程“的意思并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World),直到它收集结束。”Stop The World“这个名字也许听起来很酷,但这项工作实际上是由虚拟机后台自动发起和自动完成的,在用户不可见的情况下把用户的正常工作的线程全部停掉。
为什么会有这个Stop The World呢?一个很切合实际的场景:你妈妈再给你打扫房间,你肯定得老老实实呆着等你妈扫完,如果他一边打扫,你一边扔垃圾,这个房间还能打扫完吗?这个确实是一个合情合理的矛盾,而且垃圾收集肯定比这个打扫房间要复杂。
从Serial收集器到Parallel收集器,再到CMS,还有G1收集器,我们看到一个个越来越优秀的(也越来越复杂)的收集器的出现,用户线程的停顿时间在不断缩短,但是仍然没有办法完全消除。所以革命尚未成功,同志仍需努力!
这样看来,Serial收集器是不是很鸡肋,但实际上到现在为止,他依然是虚拟机运行在Client模式下的默认新生代收集器。它也有有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。在桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代,停顿时间完全可以控制在几十秒最多一百毫秒以内,只要不是频繁发生,这点停顿是可以接受的。 -
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serival收集器完全一样。
ParNew收集器除了多线程收集之外,其他的与Serial收集器相比并没有太多创新之处,但她却是许多运行在Server模式下的虚拟机首选的新生代收集器,其中有一个与性能无关但很重要的原因是。除了Serial收集器外,目前只有他能与CMS收集器配合工作。
ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果。甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中不能百分比地保证能超越Serial收集器。。当然随着CPU数量的增加,他对于GC时系统资源的利用还是蛮有好处的。它默认开启的收集线程数与CPU的数量相同有,在CPU非常多的情况下,可以使用-XX:ParallelGCThreads参数来限制4垃圾收集的线程数。 -
Parallel Scavenge收集器
Parallel Scavenges收集器也是新生代收集器,它也是使用复制算法的收集箱,又是并行的多线程收集器,看上去和ParNew都一样,那为什么还要有Parallel Scavenge收集器呢?
Parallel Scavenge收集器的特点是他的关注点与其他的收集器不同,CMS等收集器地关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器地目的是达到一个可控的吞吐量。所谓的吞吐量就是CPU用于运行用户代码的时间也CPU总消耗的时间,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾回收时间),如果虚拟机总共运行了100分钟,其中垃圾收集花掉了1分钟,那么吞吐量就是99%。
停顿的时间越短就越适合需要和用户交互地程序,良好的响应速度能够提升用户的体验:而高吞吐量则可以最高效率地利用CPU的时间,尽可能快的完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。 -
Serial Old收集器
它是Serial收集器的老年代版本,也是一个单线程收集器,使用”标记-整理”算法,这个收集器的主要意义也是被Cilent模式下地虚拟机使用。如果Server模式下,它主要还有两大用途:一个是与Parallel Scavenge收集器搭配使用,另一个就是作为CMS收集器的后背预案,在并发收集发生Concurrent Mode Failure的时候使用。 -
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK1.6中才开始提供地。在此之前,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old收集器别无选择。由于单线程的老年代收集器在服务端应用性能上的拖累,即使使用了Parallel Scavenge收集器也未必能够在整体应用上获得吞吐量最大化的效果,又因为老年代收集中无法充分利用服务器多CPU地处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至还不如ParNew加CMS的组合给力。
直到Parallel Old收集器出现后,吞吐量优先收集器终于有名副其实地应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Old加Parallel Scavenge收集器。 -
CMS收集器
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分Java应用都集中在B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望停顿时间最短,以给带来较好的体验。CMS收集器就非常适合这类应用的需求。
它是一款基于“标记-清除”算法实现的,它的运作过程比较复杂,可以分为四个步骤,包括:1.初始标记 2.并发标记 3.重新标记 4.并发清除
其中初始标记、重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅只是标记一下GC Roots能够直接关联的对象,速度很快,并发标记阶段就是GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因为用户程序继续运行而导致标记产生变动地那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记稍微长一些,但是远比并发标记的时间短。
CMS是一款优秀的收集器,它的最重要的有点在名字上已经体现出来了:并发收集、低停顿。但是CMS还远达不到完美的程度,它有以下三点显著的缺点:
1.CMS收集器对CPU资源非常敏感。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并 发回收时垃圾收集线程最多占用不超过25%地CPU资源。但是当CPU不足四个时,那么CMS对用户程序的影响就可能变得很大。为了解决用户程序执行速度慢的问题,虚拟机提供了一种“增量式并发收集器”地CMS收集器变种有,所做的事情和单CPU年代PC机操作系统使用抢占式来模拟多任务机制的思想一样,就是在并发标记和并发清理的时候让GC线程、用户线程交替运行,尽量减少GC线程独占资源的时间,这样的话虽然垃圾回收地过程会变更长,但是对用户程序的影响会显得更少一点。
2.CMS收集器无法处理浮动垃圾。
3.因为用的是标记-清除地算法,这就意味着收集结束时会产生大量空间碎片。 -
G1收集器
G1收集器是基于“标记-整理”算法实现的收集器,也就是说他不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。二是它可以非常精确的控制停顿,既能让使用者明确指定在一个长度M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java的垃圾回收特征了。
G1收集器可以实现在基本不牺牲吞吐量的前提下完成低停顿的内存回收,这样由于它能够极力避免全区域的垃圾回收,之前的收集器进行收集的范围都是整个新生代和老年代,而G1收集器将整个堆(包含新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的手机时间,优先回收垃圾最多的区域。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内可以获取最高的效率。