垃圾收集器

前面讲了垃圾回收的的算法,属于理论。这一篇将讲一下他们的实现,即垃圾收集器。

default

新生代收集器

Serial 收集器

最基本的发展历史最悠久的收集器。

在它进行垃圾收集时,必须暂停所有其他的工作线程。如图

serial

是运行在Client模式下虚拟机的新生代默认收集器,简单而高效。
缺点是:在用户不可见的情况下把用户的正常工作的线程全部停掉,用户体验差一点,不过保持停顿在很短的时间,还是可以接受的。

ParNew 收集器

多线程收集,原理类似Serial收集器。如图

parnew

是运行在Server模式下虚拟机的新生代默认收集器。适用于多 CPU 环境下。

Parallel Scavenge收集器

追求吞吐量(CPU运行时间与消耗的时间比值)。

以上三个新生代收集器,都是采用复制算法进行收集的

老年代收集器

Serial Old收集器

单线程收集器,使用标记-压缩算法。如图

serialold

Parallel Old收集器

parllelold

CMS (Concurrent Mark Sweep) 收集器

以获取最短回收停顿时间为目标。整个过程中的并发标记和并发清除过程收集器。线程可以与用户线程一起工作。

cms

整个过程可以分为 4 个步骤:

1.初始标记

2.并发标记

3.重新标记

4.并发清除

从上图中可以看出,以上初始标记和重新标记仍然需要 Stop The World。初始标记仅仅标记 GC Root 能直接关联到的对象,速度很快;并发标记就是进行 GC Roots Tracing 的过程,重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,虽然它也需要 Stop The World, 但是其耗费的时间与并发标记相比起来是很短暂的(毕竟一大部分垃圾已经收被标记好了,在并发标记阶段产生的垃圾也不会太多)。

由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。

缺点:

1.CMS 收集器对于 CPU 资源非常敏感,当CPU数量不足4个时,CMS对于用户的影响可能变得很大。

2.CMS收集器无法处理浮动垃圾。由于CMS并发清理阶段用户线程仍然还在继续运行,自然还会有垃圾不断产生,这部分垃圾就叫做浮动垃圾。

3.CMS基于标记-清除算法来实现垃圾回收。这种算法的缺点就会产生许多的内存碎片。

内存分配和回收策略

对象优先在Eden区分配

对象的内存分配,就是在堆上分配。对象主要分配在新生代的Eden区。当Eden区没有足够空间分配时,虚拟机将发起一次 Minor GC。

大对象直接进入老年代

所谓大对象,即需要大量连续内存空间的 Java 对象,最典型的是那种很长的字符串以及数组。所以写程序时尽量不分配一些生命周期不长的大对象。

长期存活的对象直接进入老年代

虚拟机给每个对象定义了一个年龄计数器。如果对象在Eden出生,并经过第一次 Minor GC后任然存活,并且能被 Survivor 容纳,则被移动到 Survivor 空间中,对象年龄设置为1,对象在Survivor空间中每熬过一次 Minor GC,年龄就增加一岁。当他的年龄增加到一定年龄(默认15岁),就会被晋升到老年代。