JVM垃圾回收-Serial收集器& ParNew收集器& Parallel收集器& CMS收集器(五)

Serial垃圾收集器(串型回收)

  • Serial GC是最基础, 历史最悠久的垃圾收集器, Jdk1.3.1之前回收新生代的唯一选择, 它是一个单线程的串行收集器, 当开始回收时, 将引起所有用户线程 STW, 直到结束
  • 在 JVM的 Client模式下, 它是默认收集器(Serial& Serial Old)

1. Serial新生代收集器

  • 复制算法

2. Serial Old老年代收集器

  • 标记-压缩算法
  • 在 JVM的 Server模式下, 1. 主要与 Parallel Scavenge(新年代并行回收)配合使用(Jdk14开始弃用). 2. 或作为 CMS(老年代并发回收)收集器的后备垃圾回收方案(Jdk14开始 CMS被弃用)

在这里插入图片描述

优点: 在单核 CPU环境中选择它, 由于没有线程间上下文切换的开销, 性能比任何收集器都要好

  • 开启参数 -XX:+UseSerialGC后, 默认开启 Serial Old老年代收集器, 也就是会同时生效

ParNew垃圾收集器(并行回收)

  1. 如果 Serial GC是新年代的单线程收集器, 那么 ParNew收集器, 则是 Serial收集器的多线程版本
  2. 针对新年代的回收
  3. 采用的复制算法, 同时垃圾回收时, 也会引起所有的用户线程的 STW
  4. 除 Serial GC外, 目前只有 ParNew GC能与 CMS收集器配合使用(Jdk14开始 CMS被弃用)

在这里插入图片描述

相对于 Serial GC优势是在多核 CPU环境下, 可以充分利用处理性能, 更快的回收垃圾, 同时提升程序的吞吐量

  • 开启参数 -XX:+UseParNewGC, 表示新年代使用 ParNew收集器, 不影响老年代
  • 设置老年代为 CMS(并发收集器) -XX:+UseConcMarkSweepGC
  • 设置线程数 -XX:ParallelGCThreads=8, 默认与 CPU的核数是相同的, 建议设置小于核数, 不建议设大于核数. 当核数大于8个 ParallelGCThreads=3 + (5 * cpu core / 8)

Parallel垃圾收集器(并行回收)

  • HotSpot的新年代中, 除了 ParNew收集器是基于并行回收以外, Parallel Scavenge收集器同样也采用了并行回收, 复制算法, 还有当垃圾回收时, 也会引起所有用户线程的 STW
  • Parallel Scavenge收集器是吞吐量优先的收集器, 区别于 ParNew收集器, 它具有自适应调节策略(如 新年代大小 Eden和 Survivor的比例, 还有晋升老年代的对象年龄等参数会被自动调整)
  • 从 Jdk6开始提供了, 与它配套的老年代收集器 Parallel Old(它也是并行回收, 采用的算法是标记-压缩算法, 同时也会引起 STW)
  • (1) Parallel Scavenge和 Parallel Old的组合, 在 Jdk8的 Server模式下是默认收集器
  • (2) 这个组合比较适合在高性能服务器使用
    * 注: 虽然可以搭配成 Parallel Scavenge + Serial Old, 不过此配套不如 ParNew + CMS, 所以如果想要使用 Parallel Scavenge就应该与 Parallel Old搭配

在这里插入图片描述

  • 开启新年代回收参数 -XX:+UseParallelGC, 开启老年代回收参数 -XX:+UseParallelOldGC. 此两参数只要开启一个, 另一个也会被自动开启
  • 设置线程数 -XX:ParallelGCThreads=8, 默认与 CPU的核数是相同的, 建议设置小于核数, 不建议设大于核数. 当核数大于8个 ParallelGCThreads=3 + (5 * cpu core / 8)
  • 设置收集器最大停顿时间 -XX:MaxGCPauseMillis=200(即 STW的时间, 单位为毫秒)
  1. 为了尽可能地将停顿时间控制在 MaxGCPauseMillis以内, 收集器在工作时会自动调整堆里的相关参数
  2. 每次停顿时间越短对于用户的体验是越好的. 但一般停顿时间短意味着回收相对频繁, 因而影响吞吐量. 作为吞吐量优先的收集器, 不宜与设置太小
  3. 该参数使用需谨慎
  • 设置垃圾回收时间占总时间的比率(吞吐量) -XX:GCTimeRatio=N, 取值范围是大于0, 且小于100的整数, 默认值为99, 也就是允许最大1%(即1 / ( 1 + N))的垃圾收集时间.
    (*) 此参与 -XX:MaxGCPauseMillis有一定矛盾性. 因此在权衡参数时要抓住性能瓶颈
  • 开启 Parallel Scavenge收集器的自适应调节策略 -XX:+UseAdaptiveSizePolicy(此参默认开启)
    (-) 自动调节新年代大小 Eden和 Survivor的比例, 还有晋升老年代的对象年龄等参数, 从此达到吞吐量, 暂停时间和内存占用之间的平衡
    (-) 在调优比较困难的场合, 可以只指定虚拟机的堆大小, 目标吞吐量 GCTimeRatio和停顿时间 MaxGCPauseMillis, 直接让虚拟机自适应调节

CMS垃圾收集器(并发回收)

  • Jdk5时 HotSpot推出了 CMS(Concurrent Mark-Sweep)收集器, 它是响应速度优先的低延迟垃圾收集器, 采用的算法是标记-清除算法, 还有依然存在 STW
  • CMS垃圾回收4个阶段:
  1. 初始标记(Initial-Mark)阶段: 标记 GC Roots能够直接关联的对象. 此阶段用户线程会引起 STW, 直到初始标记完成
  2. 并发标记(Concurrent-Mark)阶段: 遍历第1阶段标记的可达对象, 进行根检查后, 再标记为活跃的对象. 此阶段是与用户线程是并发进行的
  3. 重新标记(Remark)阶段: 由于在并发标记阶段中, 用户线程和垃圾收集线程(标记阶段)同时或交叉运行, 从而导致了标记产生变动的那一部分的对象标记记录做修正. 此阶段用户线程会引起 STW, 直到标记完成
  4. 并发清楚(Concurrent-Sweep)阶段: 回收在标记阶段判断为不可达的对象, 由于用的标记-清除算法, 所以不需要移动存活对象, 因此也是并发进行的
  • 以上阶段中并发标记和并发清除阶段是最耗费时间的, 但两个都是并发回收(非独占式), 所以整体的交互是低停顿的

在这里插入图片描述

  • 优点: 并发回收, 低延迟
  • 缺点:
  1. 由于采用的是标记-清除算法, 所以会产生内存碎片
  2. 在并发回收时, 虽然不会一次性停顿长时间影响交互体验, 但是因为回收线程与用户线程交替并发工作的, 所以吞吐量会下降
  3. 在并发标记阶段, 对于浮动垃圾是有遗漏的, 无法及时回收只能下次 GC时做标记, 并回收

* 注: 由于 CMS垃圾回收是并发进行的, 所以在回收过程中, 还应该确保应用程序有足够的内存可用. 因此, CMS收集器不是等到老年代满后再进行收集的, 而是当堆内存使用率达到某一阈值时, 便开始进行回收. 要是运行期间预留的内存依然不够, 就会出现一次 Concurrent Mode Failure, 然后虚拟机会自动采用 Serial Old收集器进行 Full GC

  • 开启 CMS垃圾收集器 -XX:+UseConcMarkSweepGC后, 将自动开启新年代的收集器 -XX:+UseParNewGC 即: ParNew(Young区) + CMS(Old区) + Serial Old(当 Concurrent Mode Failure时用于触发 Full GC)的组合
  • 设置堆内存使用率的阈值 XX:CMSInitiatingOccupanyFraction=N, 一旦到达该阈值即触发回收
    (-) Jdk6之前默认值为68%, 之后92%
    (*) 如果内存增长缓慢, 则可以设置一个稍大的值, 可以有效降低 CMS的触发频率. 反之, 如果应用程序内存增长很快, 则应该降低这个阈值, 以避免频繁触发老年代串行收集器
  • 开启 -XX:+UseCMSCompactAtFullCollection 当 Full GC后再对内存空间进行压缩整理, 由于压缩整理过程无法并发执行, 因此产生的问题是停顿时间变的更长(Jdk8开始此参已过时)
  • 设置次数 -XX:CMSFullGCsBeforeCompaction=N在上面的配置开启的前提下, 执行了N次 Full GC后对内存空间进行压缩整理(Jdk8开始此参已过时)
  • 设置 CMS的线程数 -XX:ParallelCMSThreads=2, 默认启动的线程数是(ParallelGCThreads + 3) / 4 = 2; ParallelGCThreads是新年代 ParNew并行收集器的线程数

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

猜你喜欢

转载自blog.csdn.net/qcl108/article/details/108889382