1.G1垃圾回收器1.1.垃圾优先(garbage first)1.2.在堆内离散的区域上进行操作1.2.1.默认大约有2048个1.2.2.代的区域不需要是连续的1.2.3.可能属于老年代

  • 1.2.3.1.并发后台线程寻找没有被引用的对象时,一些区域会比其他区域有更多的垃圾

1.2.4.可能属于新生代1.3.并发回收器(concurrent collector)1.3.1.标记老年代中不使用的对象和应用程序线程同时发生(它们同时运行)1.3.2.并不是完全并发的

  • 1.3.2.1.新生代的标记和压缩仍需要暂停所有应用程序线程

  • 1.3.2.2.老年代的压缩也是在应用程序线程暂停期间发生的

1.4.停顿1.4.1.较长的Full GC停顿

  • 1.4.1.1.理想情况下,你已经优化得足够好,就不会发生这种情况

1.4.2.较短的Young GC停顿

  • 1.4.2.1.包括回收和压缩部分老年代的混合回收

1.4.3.非常短的标记线程停顿1.5.4个逻辑操作1.5.1.新生代回收

  • 1.5.1.1.Young GC

1.5.2.后台并发标记周期

  • 1.5.2.1.第一个阶段

    • 1.5.2.1.1.JDK 8中被称为初始标记(initial mark)

    • 1.5.2.1.2.JDK 11中被称为并发开始(concurrent start)

      • 1.5.2.1.2.1.大小以区域为单位,而不是MB

      • 1.5.2.1.2.2.一个新的区域:巨型对象区域,是老年代的一部分

    • 1.5.2.1.3.暂停所有的应用程序线程

  • 1.5.2.2.重新标记(remark)阶段

    • 1.5.2.2.1.会暂停应用程序线程,不过时间通常比较短
  • 1.5.2.3.正常的清理(cleanup)阶段

    • 1.5.2.3.1.会暂停应用程序线程,不过时间通常比较短

1.5.3.Mixed GC

  • 1.5.3.1.混合垃圾回收

  • 1.5.3.2.执行正常的新生代回收时,也会回收后台扫描时标记的一些区域

  • 1.5.3.3.在JDK 11中,首次Mixed GC被标记为Prepared Mixed,紧接着是并发清理

  • 1.5.3.4.将执行多次,持续到(几乎)所有标记的区域都完成回收,恢复常规的Young GC周期

1.5.4.必要的Full GC

  • 1.5.4.1.并发模式失败(concurrent mode failure)

    • 1.5.4.1.1.老年代在这个标记周期完成之前被填满了

    • 1.5.4.1.2.应该增加堆的大小

    • 1.5.4.1.3.G1 GC的后台处理必须更快

    • 1.5.4.1.4.必须优化标记周期以更快地运行

  • 1.5.4.2.晋升失败(promotion failure)

    • 1.5.4.2.1.已经开始执行Mixed GC以清理老年代的区域。在它还没有清理出足够的空间之前,有太多的对象从新生代晋升,以至于老年代的空间还是用完了

    • 1.5.4.2.2.混合回收需要执行得更快

    • 1.5.4.2.3.每次新生代回收都需要处理更多的老年代区域

  • 1.5.4.3.疏散失败(evacuation failure)

    • 1.5.4.3.1.堆已经非常满了或者碎片化很严重

    • 1.5.4.3.2.增加堆的大小

  • 1.5.4.4.巨型对象分配失败(humongous allocation failure)

  • 1.5.4.5.元数据GC阈值(metadata GC threshold)

    • 1.5.4.5.1.元空间本质上是一个独立的堆,并且独立于主堆进行回收

    • 1.5.4.5.2.在JDK 8中,当它需要进行回收时,G1 GC会在主堆上执行Full GC(紧跟着新生代回收)

    • 1.5.4.5.3.在JDK 11中,元空间可以被回收,也可以调整大小,而不必进行Full GC

1.6.运行G1的JVM经过良好优化后应该只经历Young GC、Mixed GC和并发GC周期2.优化G1 GC2.1.目标是确保没有因并发模式失败或疏散失败而产生Full GC2.1.1.从设置合理的停顿时间目标开始2.2.在JDK 8中执行Full GC时,使用的是单线程,这就会造成停顿时间比平常更长2.3.在JDK 11中,Full GC由多个线程执行,从而使停顿时间更短2.4.增加老年代的大小,增加堆空间的总大小,或者调整分代比例2.5.增加后台线程的数量(假设有足够的CPU)2.6.更频繁地执行G1 GC后台活动2.7.增加Mixed GC周期的工作量2.8.-XX:MaxGCPauseMillis=N标志2.8.1.该标志有默认值,即200毫秒2.9.优化G1后台线程2.9.1.-XX:ParallelGCThreads=N标志

  • 2.9.1.1.影响应用程序线程暂停阶段的线程数量

2.9.2.-XX:ConcGCThreads=N标志

  • 2.9.2.1.影响用于并发标记的线程数量

  • 2.9.2.2.如果有额外的CPU可用

  • 2.9.2.3.计算方式

    • 2.9.2.3.1.ConcGCThreads = (ParallelGCThreads + 2) / 4

    • 2.9.2.3.2.基于整数的

2.10.优化G1 GC的运行频率2.10.1.G1 GC提前开始后台标记周期,也可以尽量减少Full GC2.10.2.当堆达到-XX:InitiatingHeapOccupancyPercent=N设定的占用率时,这个周期才会开始2.10.3.-XX:InitiatingHeapOccupancyPercent=N

  • 2.10.3.1.默认值是45,表示老年代占整个堆的比例

  • 2.10.3.2.为了让后台线程运行得更频繁

2.11.优化G1 GC的Mixed GC周期2.11.1.在Mixed GC周期中处理更多的区域2.11.2.-XX:G1MixedGCCountTarget=N标志

  • 2.11.2.1.处理区域时Mixed GC周期的最大总次数

  • 2.11.2.2.混合周期的数量上限

  • 2.11.2.3.默认值是8

  • 2.11.2.4.减小该值有助于解决晋升失败的问题(代价是Mixed GC周期的停顿时间更长)

2.11.3.MaxGCPauseMillis设定

  • 2.11.3.1.GC可接受的最大停顿毫秒数

  • 2.11.3.2.增加MaxGCPauseMillis标志的值,可以在每次Mixed GC期间回收更多的老年代区域

3.JDK 12引入的回收器3.1.现存的并发回收器并不是完全并发的3.1.1.G1 GC和CMS回收器都没有新生代的并发回收,回收新生代需要暂停所有应用程序线程3.1.2.没有进行并发压缩3.2.Z垃圾回收器(Z garbage collector,ZGC)3.2.1.在JDK 11中首次出现3.2.2.AdoptOpenJDK构建的JVM(或者你自己从源码编译的JDK)包含3.2.3.Oracle构建的JVM包含3.3.Shenandoah垃圾回收器3.3.1.在JDK 12中首次出现3.3.2.已经被向后移植到了JDK 8和JDK 11中3.3.3.AdoptOpenJDK构建的JVM(或者你自己从源码编译的JDK)包含3.4.-XX:+UnlockExperimentalVMOptions3.4.1.默认情况下是false3.5.-XX:+UseZGC3.6.-XX:+UseShenandoahGC3.7.都可以并发压缩堆3.7.1.可以在不暂停所有应用程序线程的情况下移动堆中的对象3.7.2.堆不再需要分代(不再有新生代和老年代了,只有一个堆)3.7.3.应用程序线程的操作延迟预期会减少(至少在很多情况下会)3.8.ZGC和Shenandoah会有在很短的时间内,所有的应用程序线程都会暂停3.8.1.目标是将这些时间保持在非常短的水平,即在10毫秒左右3.9.并发压缩对延迟的影响3.9.1.垃圾回收的停顿一般是造成延迟异常的最大原因3.10.并发压缩回收器对吞吐量的影响3.10.1.并发压缩回收器通常会比G1 GC后台线程执行更多的后台处理3.10.2.没有足够的CPU周期,回收器也会出现之前看到的并发失败,最终发生Full GC3.10.3.有足够的CPU,那么使用这两种回收器时的吞吐量将高于G1 GC或Throughput回收器的吞吐量4.Epsilon回收器4.1.JDK 11的一个什么都不做的回收器4.1.1.为JDK内部测试设计的4.1.2.对象永远不会从堆中回收,当堆被填满时,你会得到一个内存溢出错误的提示4.2.你确定程序需要的内存永不会比你提供的大4.3.一旦遇到了适用Epsilon回收器的情况,它会带来很好的性能提升4.4.两种情况下是有用的4.4.1.存活时间非常短的应用程序4.4.2.特意编写的、重复使用内存并且永远不执行新分配的应用程序

  • 4.4.2.1.在某些内存受限的嵌入式环境中很有用

4.5.-XX:+UnlockExperimentalVMOptions4.6.-XX:+UseEpsilonGC