4. 垃圾回收器

4.1. 串型

  • 单线程,垃圾回收时把其他线程都暂停,
  • 使用场景:堆内存较小,适合个人电脑

image.png

  • -XX:+UserSerialGC = Serial + SerialOld,也是就是说年轻代和老年代都用串型化机制,此时年轻代使用的标记-复制算法,老年代采用的是标记-整理算法。
  • [ ] 安全点?

    4.2. 吞吐量优先

  • 多线程

  • 堆内存较大,需要多核CPU支持
  • 让单位时间内STW的时间最短 0.2 0.2 =0.4

image.png

  • ParallelGC:垃圾回收线程是并行的,但是并行执行垃圾回收期间**不允许用户线程执行**。 | 参数 | 作用 | 详细说明 | | —- | —- | —- | | -XX:+UseParallelGC
    -XX:+UseParallelOldGC | 新生代(标记-复制算法)
    老年代(标记-整理算法)
    并行垃圾回收策略开关
    | JDK1.8默认开启并行;
    这两个参数开启其中一个,另一个就自动开启 | | -XX:+UseAdaptiveSizePolicy | 自适应策略开关 | ParallelGC模式下,这个开关只要代开就会去调整伊甸园与from和to的占比,以及堆的大小,还有晋升阀值的大小,以达到调整目标是:GCTimeRatio和MaxPauseMills | | -XX:+GCTimeRatio =ratio | 设置GCTime占比(百分比)
    默认99
    和MaxGCPauseMills目标相反 | GCTimeRatio参数的值应当是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5%(即1/(1+19)),默认值为99,就是允许最大1%(即1/(1+99))的垃圾收集时间。 | | -XX:+MaxGCPauseMills=ms | 设置最大GC暂停时间(毫秒数)
    默认200ms
    和GCTimeRatio目标相反 | 仅在Parallel Scavenge收集器下使用 | | -XX:ParallelGCThreads=n | 调整垃圾回收线程数 | 默认情况下垃圾回收线程数等于CPU的个数 |

补充:

  • 吞吐量= 运行客户代码时间/(运行客户代码时间+垃圾收集时间)
  • 参数= 运行客户代码时间/垃圾回收时间

    4.3. 响应时间优先

  • 多线程

  • 堆内存较大,需要多核CPU支持
  • 尽可能的让单次STW的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

image.png

  • 初始标记:
    • 只会标记根对象,会产生SWT,只是暂停时间很多;
  • 并发标记:
    • 和用户线程并发执行,不需要SWT,响应时间很短;
  • 重新标记:
    • 因为并发标记的时候用户线程也在运行,这时候对垃圾回收产生了影响,所以这时候需要暂停用户线程进行重新标记,这时候会产生SWT。
  • 并发清理:
    • 用户线程和清理是并发执行的。

只有初始标记和重新标记会产生SWT。
CMSGC:垃圾回收器运行期间也允许用户线程同时运行,也就是说垃圾回收线程和用户线程都会并发执行,
进一步减少SWT时间,CMS在某些姐多是不需要SWT的。

参数 作用 详细说明
-XX:+UseConcMarkSweepGC
配合
-XX:+UseParNewGC
并发标记-清理(工作在老年代)

(新生代)
注意:因为采用的是标记-清除算法,所以有可能产生碎片比较多的,而此时又有比较多的对象产生,这时候后就会造成并发失败,即Concurrent Mode Failure这时候会采用SerialOld模式(串型模式),一旦发生并发失败,垃圾回收的时间会一下子飙升上来,这也是CMS存在的一个很大的问题。
-XX:ParallelGCThread = n
-XX:ConcGCThreads=threads
并行垃圾回收线程数

并发垃圾回收线程数 | 一般情况和CPU核数一样
一般设置为并行线程数的1/4 | | -XX:CMSInitiatingOccupancyFraction=percent | 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。
默认值68% | 因为在执行并行清理的时候,用户的线程也在运行,这时候会产生新的垃圾(浮动垃圾),那就需要预留出一定的空间存储这一部分垃圾等到下一次垃圾清理的时候去处理。(因为不能等到内存不足了去执行垃圾回收,那样这些新的垃圾就没处放了)
仅在使用CMS收集器的时候生效 | | -XX:+CMSScavengeBeforeRemark | 设置之后在进行重新标记之前进行新生代的一次垃圾回收 | 重新标记的时候,有可能新生代的对象引用了老年代的对象。重新标记的时候会扫描整个堆,新生代创建的对象比较多,而且这部分对象都需要清理掉,而这些对象又要去引用老年代的对象,这样在标记的时候,会做很多的无用功,所以设置这个开关之后可以在重新标记的时候进行一次新生代的垃圾回收,从而提高效率。 |

4.4. G1

  • 定义:Garbage First
    • 2004年论文发布
    • 2009年JDK 6u14体验
    • 2012年JDK 7u4官方支持
    • 2017年JDK9默认支持,并且废弃了CMS垃圾回收器
  • 使用场景:
    • 同时注重吞吐量(Throughput)和低延迟(Low latency)
    • 适合超大堆内存,会将堆划分为多个大小相等的Region;
    • 整体上是标记-整理算法,两个区域之间是标记-复制算法。
  • 相关JVM参数 | 参数 | 作用 | 详细说明 | | —- | —- | —- | | -XX:+UseG1GC | 开启G1 | 1.8不是默认,9不需要显示启用 | | -XX:G1HeapRegionSize=size | 设置Region的大小 | 必须设置成1、2、4、8这样的大小
    化整为零 | | -XX:MaxGCPauseMills=time | 设置暂停时间
    默认值为200ms | |

4.4.1. G1垃圾回收阶段

image.png

  1. 新生代收集
  2. 新生代收集+并发标记
  3. 混合收集

    4.4.2. Young Collection

    image.png
    G1把堆划分成了一个个的Region,而每个区域都可能是伊甸园,幸存区或者老年代。伊甸园区被占满会触发Young Collection。会产生SWT,新生代垃圾回收就会以Copy的技术复制到幸存区。
    image.png
    等再工作一段时间,幸存区的对象多了,或者幸存区的对象有一部分超过了阀值,就会触发GC,将一部分对象复制到老年代,当然不够年龄的会拷贝到另一个幸存区。
    image.png

    4.4.3. Young Collection + Concurrent Mark

  • Young GC是会进行GC Root的初始标记
  • 老年代占用堆空间比例达到阀值时,进行并发标记(CM),由下面的JVM参数决定:

-XX:InitatingHeapOccupancyPercent = percent (默认值45%)。【老年代占整个堆空间的45%时,触发并发标记】
image.png

4.4.4. Mixed Collection

会对E、S、O进行全面垃圾回收

  • 最终标记(Remark)会SWT
  • 拷贝存活(Evacuation)会SWT

-XX:MaxGCPauseMills=ms
image.png