阅读 236

GC垃圾回收器

这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战

WangScaler: 一个用心创作的作者。

声明:才疏学浅,如有错误,恳请指正。

之前在文章GC垃圾回收讲过垃圾回收的几种算法,那么今天就来带大家看看算法的落地实现—>垃圾回收器。

  • Serial:串行垃圾回收器,使用一个线程进行垃圾回收,回收时将暂停用户的所有线程。适合用于单线程环境。
  • Parallet:并行垃圾回收器,多个垃圾回收线程并行处理,不过也会暂停所有的用户的线程。适合用于计算/大数据等场景。
  • CMS:并发垃圾回收器,用户的线程和垃圾回收的线程同时执行。适合用于对响应时间有要求的场景。
  • G1:G1垃圾回收器,
  • ZGC

串行垃圾回收器

单线程垃圾回收器,是历史发展最久远的垃圾收集器,新生代可采用Serial Copying(复制算法)进行回收,老年代可采用Serial Old(标记压缩法)进行回收,一般不会使用Serial Old垃圾回收器,因为它已经被遗弃。

image-20210831153922677.png

我可以看到,当进行垃圾回收时,必须暂停所有工作进程,即STW(Stop -the-world),因为没有线程交互,简单高效但时间可能很长。

并行垃圾回收器

相比于串行垃圾回收器,增加了几个线程(默认等同于cpu数量,可通过JVM参数-XX:ParallelGCThreads来改变线程的数量),同样会产生STW。新生代可采用ParNew(复制算法)或者Parallel Scavenge(复制算法),老年代可采用Parllel Old(标记压缩法)。ParNew一般配合CMS使用。

image-20210831155759786.png

其中Parallel Scavenge关注于吞吐量(运行用户代码的时间/(运行用户代码的时间+垃圾回收时间)),也被称为吞吐量优先收集器。有GC的自适应调节机制,动态的调整新生代的大小(-Xmn)、Eden和Survivor比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,可通过设置-XX:+UseAdaptiveSizePolicy参数开启自适应调节机制,来获得最合适的停顿时间(-XX:MaxGCPauseMillis)或者最大的吞吐量(-XX:GCRatio),开启UseParallelGC同样也会开启自适应调节机制。

并发垃圾回收器

CMS并发回收器是最常用的垃圾回收器(G1出现之前),非常适合互联网或者B/S服务器上,它只回收老年代的垃圾,采用的标记清理算法。

主要有四个步骤

  • 1、初始标记:标记可达存活的对象(即GC-Root可达的对象)。
  • 2、并发标记:GC Roots Tracing,标记初始标记之后发生改变的对象(新生代晋升到老年代、老年代分配对象、更新老年代对象的引用关系等对象)进行重新标记,将对象所在的Card标识为Dirty。
  • 3、重新标记:对标记期间产生的对象进行判断,进行标记。
  • 4、并发清理:清除不可达的对象。

image-20210831163032401.png

从图中可以看出,初始标记和重新标记都会造成用户线程的终止即STW。而并发标记和并发清理的过程和用户线程可以同时进行,所以减少了停顿的时间,是一种以获取最短回收停顿时间为目标的收集器。

在并发标记阶段会因为浮动垃圾而产生concurrent mode failure的问题,从而导致Full GC。又因为并发垃圾收集器采取的是标记清除算法,所以不可避免的产生空间碎片,当大对象申请空间时,往往会提前引起Full GC。

G1收集器

从jdk7诞生,到jdk9之后称为默认的面向服务端的垃圾回收器,大大减少了垃圾回收的停顿时间,而且增加了预测机制,允许用户指定期望的停顿时间(-XX:MaxGCPauseMillis)。将原来的新生区(Eden和Survivor)和老年区(Tenured)改变成一个个大小一样的region(每个分区1m-32m,2的幂通过-XX:G1HeapRegionSize设置,默认为2048个分区),所以整体上新生区、老年区不再泾渭分明,而变成了有些region是新生区,有些region是老年区。G1收集器整体上采用标记压缩算法,局部采用复制法。

image-20210831194418827.png

新的分区将变成上图的形式,其中Humongous区用来存储大的对象,如果一个Humongous区不够用,则将采用多个进行存储。

image-20210831195004677.png

默认堆占用45会进行GC,可通过-XX:InitiatingHeapOccupancyPercent修改,-XX:ConcGcThreads修改GC线程数、-XX:G1ReservePercent设置预留空闲空间占内存的比值,默认是10%。

其他

可通过命令java -XX:+PrintCommandLineFlags -version查看默认的垃圾回收器,更多JVM命令或者更换别的垃圾回收器可参考往期文章常见的JVM参数。java8默认的垃圾回收期是并行垃圾回收器。不过可设置的Jvm参数有UseSerialGC(Serial+Serial Old)、UseParNewGC(ParNew+Serial Old,会提示Serial Old已被遗弃,不再推荐)、UseParallelGC(Parallel Scavenge+Parllel Old)、UseParallelOldGC(Parallel Scavenge+Parllel Old)、UseConcMarkSweepGC(ParNew+CMS+Serial Old,因为CMS和用户的线程同时进行同样会占用堆内存,当堆内存不够用时,CMS出错之后将采用Serial Old收集器进行回收)、UseG1GC以及被遗弃的参数UseSerilalOldGC(Serial Old)。

以下是其中回收器结合使用的方式,红线表示java8之后,不再推荐的方式。

image-20210831174536040.png

从上图可以看出,Serial垃圾回收器、ParNew垃圾回收器均可可与CMS回收器配合使用。

使用场景:

参数场景
UseSerialGC单CPU或小内存的单机程序
UseParallelGC/UseParallelOldGC多CPU、大吞吐量
UseParNewGC/UseConcMarkSweepGC追求响应速度

总结

  • 1、G1是可以兼顾新生代和老年代的垃圾收集器。
  • 2、Serial、ParNew、Parallel Scavenge三种收集器是新生代的垃圾收集器。
  • 3、Serial Old、Parllel Old、CMS三种收集器是老年代垃圾收集器。
  • 4、UseParallelGC和UseParallelOldGC都是新生代使用Parallel Scavenge,老年代使用Parllel Old。两者的区别是后者更偏向于老年代使用并行垃圾回收期,因为当使用-UseParallelOldGC时,只将老年代的Parllel Old替换为串行垃圾回收期SerialOld,而新生代保持不变。
  • 5、G1和CMS相比的优势是,G1没有内存碎片,而且可以控制停顿时间。

来都来了,点个赞再走呗!

关注WangScaler,祝你升职、加薪、不提桶!

文章分类
后端
文章标签