JVM知识总结-GC收集器

JVM垃圾收集器目前实现有以下几类:

  1. 新生代收集器:Serial, ParNew, Parallel Scavenge;
  2. 老年代收集器:CMS, Serial Old, Parallel Old;
  3. 跨界收集器:G1
    如下图所示,存在连接线的收集器表示可以搭配使用(图引自《深入理解JVM虚拟机》)

垃圾收集场景下的并发和并行

  1. 并行(Parallel):多条垃圾收集线程并行工作,但用户线程处于等待状态;
  2. 并发(Concurrent):用户线程与垃圾收集线程同时执行,但并不一定是并行,也可能是交替执行,用户程序和垃圾收集运行在不同的CPU上;

收集器介绍

  1. Serial收集器:仅仅使用一个线程去进行垃圾收集工作,且垃圾收集线程工作时,需要暂停用户正常工作的线程,即STOP THE WORLD,适用于单CPU的场景,client模式下JVM默认新生代收集器;

  2. ParNew收集器:Serial收集器的多线程版,Server模式JVM新生代默认收集器,一般与CMS搭配使用;

  3. Parallel Scanvenge收集器:多线程并行新生代收集器,与ParNew的区别在于此收集器更加关注对系统吞吐量的提升,吞吐量=用户线程运行时间/系统运行总时间,比较适合用在CPU密集型的应用程序上;

  4. Serial Old收集器:Serial收集器的老年代版本,使用标记-整理算法,主要用于client模式下,可以与Parallel Scanvenge收集器搭配使用;

  5. Parallel Old收集器:Parallel Scanvenge的老年代版本,使用标记-整理算法,与Parallel Scanvenge搭配使用;

  6. CMS收集器:以获取最短回收停顿时间为目标的收集器,使用标记-清除算法,每个回收周期可总结为以下六步:

    • 初始标记:需要STW,但仅标记与GC ROOTS能直接关联到的对象,因此耗时很短;
    • 并发标记:不需要STW,使用一个或多个进程进行GC ROOTS Tracing,耗时较长;
    • 再次并发标记:不需要STW,使用一个进程进行GC ROOTS tracing,弥补上个步骤执行时对象的变化;
    • 重新标记:需要STW,主要是对并发标记期间程序运行对象发生变动的补充,停顿时间比初始标记稍长,但也远短于并发标记;
    • 并发清除:不需要STW,使用一个进程不可达对象进行回收,耗时较长;
    • 重置:不需要STW,使用一个进程resize heap区域,并为下次周期准备数据;
      cms收集器有以下缺点:
    • 对CPU资源敏感,由于占用了一部分线程资源会导致应用程序执行变慢,虽然使用增量式并发收集器(Incremental-CMS)解决线程独占的问题,让收集线程和应用程序线程交替执行,但效果一般,且官文明确表示该模式会在未来版本中被移除;
    • 无法处理浮动垃圾(Floating Garbage),即在垃圾清理的过程中,依然会有垃圾对象产生,这部分垃圾对象被称为浮动垃圾,因此CMS收集器需要在老年代为应用程序预留足够的空间备用,而不能等到老年代几乎被塞满后再执行,1.6中CMS的默认启动阈值为92%,当CMS运行时预留的内存无法满足应用程序使用,会抛出"Concurrent Mode Failure",VM将启用Serial Old收集器来重新进行老年代的垃圾收集
    • 由于使用的是标记-清理算法,因此会产生大量的空间碎片,当碎片过多无法保存大对象时,就会触发full GC,CMS提供了两个参数,UseCMSCompactAtFullCollection和CMSFullGCsBeforeCompation,前者指定在full GC时进行碎片整理,后者指定每隔几次full GC会进行一次碎片整理,默认值为0;
  7. G1收集器:官方最先进的垃圾收集技术,G1将java堆划分为多个大小相等的独立区域(Region),虽然还保留新生代、老年代的概念,但不再物理隔离,他们都是一部分Region(不需要连续)的集合具备如下特点:

    • 并行与并发:充分利用多CPU的硬件优势,缩短STW时间;
    • 分代收集:使用不同的方式收集新建对象、熬过多次GC的旧对象获取更好的收集效果;
    • 空间整合:整体看来G1是基于标记-整理算法的,收集期间不会产生内存空间碎片;
    • 可预测的停顿:G1可以建立可预测的停顿时间模型,能让使用者指定M毫秒内垃圾收集时间不超过N毫秒,G1在后台维护一个有限列表跟踪每个Region的回收价值,每次根据允许的垃圾收集时间优先回收价值最大的Region;
      G1需要解决的问题:
      G1"化整为零的"方式,给垃圾回收时的可达性分析带来了麻烦,因为一个Region里的对象可能和其他所有Region中的对象有引用关系,为了避免全堆扫描,G1使用Remembered Set记录对象的引用信息,在进行内存回收时加入Remembered Set即可不进行全堆扫描并不会有遗漏; G1的运行步骤:
    • 初始标记:需要STW,标记GC Roots能直接关联到的对象,并修改TAMS(Next Top at Mark Start)值,让下一阶段用户程序运行时在正确的Region中创建对象;
    • 并发标记:不需要STW,进行GC Tracing可达性分析,耗时较长;
    • 最终标记:需要STW,对并发标记的遗漏进行补充,并将此期间内对象的变化记录到Remembered Set Logs,并合并到Remembered Set中;
    • 筛选回收:对各个Region的回收价值排序,根据用户期望的GC停顿时间制定回收计划,可以与用户程序并发执行,但停顿用户线程会大幅提高回收效率

猜你喜欢

转载自my.oschina.net/u/580449/blog/2870429