携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第43天,点击查看活动详情
老年代收集器
- Serial Old收集器:老年代收集器,单线程收集器,串行,使用"标记-整理"算法(整理的方法是Sweep(清理)和Compact(压缩),
- Parallel Old 收集器
多线程机制与Parallel Scavenge差不错,使用标记整理(与Serial Old不同,这里的整理是Summary(汇总)和Compact(压缩),汇总的意思就是将幸存的对象复制到预先准备好的区域,而不是像Sweep(清理)那样清理废弃的对象)算法,在Parallel Old执行时,仍然需要暂停其它线程。Parallel Old在多核计算中很有用。这个收集器是在JDK 1.6中,与Parallel Scavenge配合有很好的效果。
参数控制: 使用-XX:+UseParallelOldGC开关控制使用Parallel Scavenge +Parallel Old组合收集器进行收集。
- CMS收集器:老年代收集器(新生代使用ParNew)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。 从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为6个步骤,其中初始标记、重新标记这两个步骤仍然需要“Stop The World”,包括:
- 初始标记(CMS-initial-mark):为了收集应用程序的对象引用需要暂停应用程序线程,该阶段完成后,应用程序线程再次启动。
- 并发标记(CMS-concurrent-mark):从第一阶段收集到的对象引用开始,遍历所有其他的对象引用,此阶段会打印2条日志:CMS-concurrent-mark-start,CMS-concurrent-mark。
- 并发预清理(CMS-concurrent-preclean):改变当运行第二阶段时,由应用程序线程产生的对象引用,以更新第二阶段的结果。 此阶段会打印2条日志:CMS-concurrent-preclean-start,CMS-concurrent-preclean。
- 下一阶段是CMS-concurrent-abortable-preclean阶段,加入此阶段的目的是使cms gc更加可控一些,作用也是执行一些预清理,以减少Rescan阶段造成应用暂停的时间. 通过两个参数来来控制是否进行下一阶段: -XX:CMSScheduleRemarkEdenSizeThreshold(默认2M):即当eden使用小于此值时; -XX:CMSScheduleRemarkEdenPenetratio(默认50%):当Eden区占用比例此比例时 在concurrent preclean阶段之后,如果Eden占用率高于CMSScheduleRemarkEdenSizeThreshold,开启'concurrent abortable preclean',并且持续的precleanig直到Eden占比超过CMSScheduleRemarkEdenPenetratio,之后,开启remark阶段, 另外,-XX:CMSMaxAbortablePrecleanTime:当abortable-preclean阶段执行达到这个时间时会结束进入下一阶段。
- 重标记CMS-concurrent-remark:由于上面三阶段是并发的,对象引用可能会发生进一步改变。因此,应用程序线程会再一次被暂停以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。这一阶段十分重要,因为必须避免收集到仍被引用的对象。
- 并发清理CMS-concurrent-sweep:所有不再被应用的对象将从堆里清除掉。
- 并发重置CMS-concurrent-reset:收集器做一些收尾的工作,以便下一次GC周期能有一个干净的状态。 尽管CMS收集器为老年代垃圾回收提供了几乎完全并发的解决方案,然而年轻代仍然通过“stop-the-world”方法来进行收集。对于交互式应用,停顿也是可接受的,背后的原理是年轻带的垃圾回收时间通常是相当短的。
优点:并发收集、低停顿 缺点:产生大量空间碎片、并发阶段会降低吞吐量
- 堆碎片:CMS收集器并没有任何碎片整理的机制,可能出现总的堆大小远没有耗尽,但因为没有足够连续的空间却不能分配对象,只能触发Full GC来解决,造成应用停顿。
- 对象分配率高:如果获取对象实例的频率高于收集器清除堆里死对象的频率,并发算法将再次失败,从某种程度上说,老年代将没有足够的可用空间来容纳一个从年轻代提升过来的对象。经常被证实是老年代有大量不必要的对象。一个可行的办法就是增加年轻代的堆大小,以防止年轻代短生命的对象提前进入老年代。另一个办法就似乎利用分析器,快照运行系统的堆转储,并且分析过度的对象分配,找出这些对象,最终减少这些对象的申请。