「CMS 收集器」
CMS(Concurrent Mark Sweep)收集器是一种「以获取最短回收停顿时间为目标的收集器」。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。
「基于“标记-清除”算法实现的」,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤,包括:
其中**初始标记、重新标记这两个步骤仍然需要“Stop The World”。**初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
「由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。」
优缺点:
并发收集、低停顿
产生大量空间碎片、并发阶段会降低吞吐量
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
「G1 收集器」(整个Java堆:包括新生代和老年代)
G1 的特点是:采用并发与并行、空间整合(整体上类似标记-整理方法,不会产生内存碎片)、分代收集、「可预测的停顿」(比CMS更先进的地方在于能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒)。
G1收集器将Java堆划分为多个大小相等的Region(独立区域),新生代与老年代都是一部分Region的集合,G1的收集范围则是这一个个Region。
整个工作流程:初始标记、并发标记、最终标记、筛选回收。初始标记阶段仅仅只是标记一下GC Roots能够直接关联的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段的用户程序并发运行的时候,能在正确可用的Region中创建对象,这个阶段需要暂停线程。并发标记阶段从GC Roots进行可达性分析,找出存活的对象,这个阶段是与用户线程并发执行的。最终标记阶段则是修正在并发标记阶段因为用户程序的并发执行而导致标记产生变动的那一部分记录,这部分记录被保存在Remembered Set Logs中,最终标记阶段再把Logs中的记录合并到Remembered Set中,这个阶段是并行执行的,仍然需要暂停用户线程。最后在筛选阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间制定回收计划。
内存分配策略
对象优先在 Eden 分配
大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。
大对象直接进入老年代
大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。
经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。
长期存活的对象进入老年代
为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。
-XX:MaxTenuringThreshold 用来定义年龄的阈值。
空间分配担保
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。
如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。