java虚拟机(三)垃圾收集器

此文章介绍垃圾收集器的种类,及各个收集的优缺点和相应的垃圾收集算法的应用

垃圾收集器(7种)

  • Serisl收集器
  • ParNew收集器
  • Parallel Scavenge 收集器
  • Serial Old 收集器
  • Parallel Old 收集器
  • CMS 收集器
  • G1垃圾回收器

Serisl收集器

  • Serisl收集器是最基本,发展历史最悠久的收集器,曾经在JDK1.3.1之前是虚拟机新生代收集的唯一选择
  • Serisl收集器是一个单线程的收集器,只会使用一个CPU或一条收集线程曲完成垃圾收集
  • 在进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束
虚拟机运行在Client模式下的默认新生代收集器
  • 与其他收集器的单线程比简单而高效
  • 对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率
  • 在用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌面应用基本上不会再大了),停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生,这点停顿是可以接受的

ParNew收集器

  • ParNew收集器其实就是Serial收集器的多线程版本
  • 它也是运行在年轻代中,在新生代中采用的是多线程模式进行垃圾收集同时也需要暂停用户线程直到垃圾收集完毕。
  • ParNew收集器在单个CPU的环境中绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中都不能百分之百的保证可以超越Serial收集器
  • 这种模式和Serial相比,CPU数量越多的情况下优势更加明,它默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境下,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数
是许多运行在Server模式下的虚拟机中首选的新生代收集器
  • 一个与性能无关但很重要的原因:除了Serial收集器外,目前只有它能与CMS收集器配合工作
  • 这种模式和Serial相比,CPU数量越多的情况下优势更加明

Parallel Scavenge 收集器

  • Parallel Scavenge 收集器是新生代收集器
  • 是使用复制算法的收集器
  • 是并行的多线程收集器
  • Parallel Scavenge 收集器关注的点与其它收集器不同,CMS等收集器关注的点是尽可能的缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量
吞吐量:吞吐量就是CPU用于运行用户代码的时间于CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那么吞吐量就是99%

适用场景

停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验
高吞吐量则可以高效率的利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务

Serial Old 收集器

  • Serial Old 是Serial收集器的老年代版本,它同样时一个单线程收集器
  • 使用"标记-清除"算法
  • 这个收集器的主要意义也是在于给Client模式下的虚拟机使用

Parallel Old 收集器

  • Parallel Old 是Parallel Scavenge 收集器的老年代版本
  • 使用多线程和"标记-整理"算法
  • Parallel Old 收集器出现后,"吞吐量优先"收集器可以组合出现,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge 加 Parallel Old收集器
CMS(Concurrent Mark Sweep)收集器
  • 收集器是一种以获取最短回收停顿时间为目标的收集器
  • 从名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“标记-清除”算法实现的

CMS收集器整个过程分为4个步骤

  • 初始标记 (CMS initial mark)
  • 并发标记 (CMS concurrent mark)
  • 重新标记 (CMS remark)
  • 并发清除 (CMS concurrent sweep)
细节
  • 其中初始标记、重新标记这两个步骤仍然需要"Stop The Word"
  • 初始标记仅仅只是标记一下GC Roots 能直接关联到的对象,速度很快,
  • 并发标记阶段就是进行GC Roots Tracing的过程
  • 而重新标记阶段这是为了修正并发标记期间,因用户程序继续运作而导致标记产生变化的一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短
  • 由于这个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作
  • CMS收集器的内存回收过程是与用户线程一起并发的执行的
CMS收集器优点
  • 并发收集,低停顿
CMS收集器缺点
  • CMS收集器对CPU资源非常敏感,其实,面向并发设计的程序都对CPU资源比较敏感。
  • 在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低
  • 当CPU不足4个时(譬如2个),CMS对用户程序的影响就可能变得很大,如果本来CPU负载较大,还要分出一半的运算能力曲执行收集器线程,就可能导致用户程序的执行速度忽然降低了50%,让人无法接受
CMS收集器无法处理浮动垃圾
  • 由于CMS并发清理阶段用户线程还在运行着,伴随程序的运行自然还会有新的垃圾产生,这一部分出现在标记过程之后,CMS无法在本次收集中处理掉他们,只好留待下次GC时再将其清理掉,这一部分垃圾就称为"浮动垃圾"
  • 由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其它收集器那样等到老年代几乎完全被填满了在进行收集,需要预留一部分空间提供给并发收集时的程序运行使用
CMS是一款基于""标记-清除"算法实现的收集器,收集结束后会产生大量的内存碎片,
  • 空间碎片过多时,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发以此Full GC
  • 为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactATFullCollection开关参数(默认就是开启的),用于在CMS收集器顶不住要进行Full GC 时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长
  • 还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)

G1垃圾回收器

Region

  • G1里面的Region的概念不同于传统的垃圾回收算法中的分区的概念。
  • G1默认把堆内存分为1024个分区,后续垃圾收集的单位都是以Region为单位的。
  • Region是实现G1算法的基础,每个Region的大小相等,通过-XX:G1HeapRegionSize参数可以设置Region的大小

Remembered Set:

  • RSet全称是Remember Set,每个Region中都有一个RSet,记录的是其他Region中的对象引用本Region对象的关系(谁引用了我的对象)
  • 记录集Remembered Sets简称RSet,用于记录对象在不同分区之间的引用关系,目的是为了加速垃圾回收的速度,主要是加速标记阶段
  • G1为了避免整堆扫描,为每个分区记录了一个RSet,记录引用分区内对象的card索引。这样标记时,仅仅需要扫描对应分区的对应card中的对象是否可达即可,极大的提升了GC效率
  • G1里面还有另外一种数据结构就Collection Set(CSet),CSet记录的是GC要收集的Region的集合,CSet里的Region可以是任意代的。在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可

是一款面向服务端应用的垃圾收集器,与CMS相比,如下特点

1. 并行与并发

G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World 停顿的时间,部分其它收集器原本需要停顿java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行

2.分代收集

与其他收集器一样,非带概念在G1中依然得以保留,虽然G1可以不需要其它收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象一伙去更好的收集效果

3.空间整合

  • 与CMS的"标记-清除"算法不同,G1从整体来看是基于"标记-整理"算法实现的收集器
  • 从局部(两个Regon之间)上来看是基于"复制"算法实现的,但是无论如何,这两个算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存
  • 这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC

4.可预测的停顿

这是G1相对CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但是G1除了追求低停顿外,还能建立可预测的停顿时间模型,通过停顿预测模型来根据用户配置的停顿时间来选择CSet的大小,从而达到用户期待的应用程序暂停时间

关于停顿时间的设置并不是越短越好。
  • 设置的时间越短意味着每次收集的CSet越小,导致垃圾逐步积累变多,最终不得不退化成Serial GC;
  • 停顿时间设置的过长,那么会导致每次都会产生长时间的停顿,影响了程序对外的响应时间
  • 通过-XX:MaxGCPauseMillis参数来设置

不计算维护Remembered Set 的操作,G1收集器的运作大致可以划分为以下几个步骤

  • 初始化标记
  • 并发标记
  • 最终标记
  • 筛选回收
详细
  • 初始化标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短
  • 并发标记阶段是从GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行
  • 最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set 中,这阶段需要停顿线程,但是可并行执行
  • 筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段可以坐到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率

下一篇java虚拟机(四)垃圾回收分代内存分配策略

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章