如果说收集算法是内存回收的方法论,那么垃圾收集器则是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现没有任何规定。这里讨论的收集器是基于JDK1.7 Update 14 之后的HotSpot虚拟机。这个虚拟机包括的收集器如下图1.1:
图1.1
图1.1展示了7种作用于不同分代的收集器。如果两个收集器之间有连线,表示它们可以搭配使用。Serial、ParNew、Parallel Scavenge 是新生代收集器,CMS 、Serial Old 、Parallel Old 是老生代收集器。G1跨越新生代老生代。 这里不是为了挑出一个最好的收集器。目前为止,没有一个最好的收集器,更没有万能的收集器。我们只能根据具体应用选择最适合的收集器。
一.Serial收集器
特点: 1.采用复制算法
2.单线程的收集器,只会使用一个CPU或一条收集线程去完成垃圾收集工作。
3.进行垃圾收集时,必须暂停其他的所有工作线程,知道它收集结束。Stop The World(STW)
优点:1.简单而高效(与其他收集器的单线程比),对于单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然获得最高的单线程收集效率。
2:对于运行在Client模式下的虚拟机是最好的一个选择。
二.ParNew收集器
特点:1.Serial收集器的多线程版本。复制算法、Stop The World、对象分配规则、回收规则和Serial一样。
2.除Serial收集器外,目前只有踏能与CMS收集器配合工作。
ParNew/Serial Old收集器运行示意图
特点:1.复制算法。并行的多线程收集器。
2.与ParNew收集器的不同在于,Parallel Scavenge收集器的目的是达到一个可控制的吞吐量。
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
特点:1.是Serial收集器的老年代版本。单线程收集器,使用 标记-整理的算法。
2.Client模式下的虚拟机使用。
3.用途:1与Parallel Scavenge收集器搭配使用. 2作为CMS收集器的后备预案。
特点:1.Parallel Old是Parallel Scavenge收集器的老年代版本。使用多线程和标记-整理算法。
2.在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加 Parallel Old 收集器。
CMS收集器(Concurrent Mark Sweep)的目标就是获取最短回收停顿时间。在注重服务器的响应速度,希望停顿时间最短,则CMS收集器是比较好的选择。
整个执行过程分为以下4个步骤:
初始标记和重新标记这两个步骤仍然需要暂停Java执行线程,初始标记只是标记GC Roots能够关联到的对象,并发标记就是执行GC Roots Tracing的过程,而重新标记就是为了修正并发标记期间因用户程序执行而导致标记发生变动使得标记错误的记录。其执行过程如下:
CMS的优点很明显:并发收集、低停顿(由于进行垃圾收集的时间主要耗在并发标记与并发清除这两个过程,虽然初始标记和重新标记仍然需要暂停用户线程,但是从总体上看,这部分占用的时间相比其他两个步骤很小,所以可以认为是低停顿的)。
尽管如此,CMS收集器的缺点也是很明显的:
G1(Garbage-First)收集器是现今收集器技术的最新成果之一,之前一直处于实验阶段,直到jdk7u4之后,才正式作为商用的收集器。
与前几个收集器相比,G1收集器有以下特点:
此外,G1收集器将Java堆划分为多个大小相等的Region(独立区域),新生代与老年代都是一部分Region的集合,G1的收集范围则是这一个个Region
G1的工作过程如下:
初始标记阶段仅仅只是标记一下GC Roots能够直接关联的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段的用户程序并发运行的时候,能在正确可用的Region中创建对象,这个阶段需要暂停线程。并发标记阶段从GC Roots进行可达性分析,找出存活的对象,这个阶段食欲用户线程并发执行的。最终标记阶段则是修正在并发标记阶段因为用户程序的并发执行而导致标记产生变动的那一部分记录,这部分记录被保存在Remembered Set Logs中,最终标记阶段再把Logs中的记录合并到Remembered Set中,这个阶段是并行执行的,仍然需要暂停用户线程。最后在筛选阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划。整个执行过成功如下:
垃圾收集器常用参数总结