Serial收集器
特点:
- 单线程
- 堆内存小
- 与其他收集器相比额外内存消耗小,适合个人电脑
JVM参数
-XX:+UseSerialGC = Serial + SerialOld
这个收集器是一个单线程工作的收集器,但它的“单线 程”的意义并不仅仅是说明它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。
因为是单线程的收集器,所以这两个收集器适合供客户端模式下的HotSpot虚拟机使用。
在服务端模式下,Serial Old可能有两种用途:一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用,另外一种就是作为CMS 收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用。
Serial/Serial Old收集器的运行过程:
ParNew 收集器
ParNew 收集器就是 Serial 收集器的多线程版本,除了同时使用多条线程进行垃圾收集之外,其余的行为都与Serial收集器完全一致,但它是许多运行在 Server 模式下的虚拟机中的首选新生代收集器,因为除了 Serial 收集器外,只有它能和 CMS 收集器搭配使用。
Parallel Scavenge收集器
Parallel Scavenge收集器是一款新生代收集器,基于标记-复制算法实现的收集器。常被称为吞吐量优先收集器。
Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。
JVM参数:
-XX:+UseParallelGC 或者-XX:+UseParallelOldGC
-XX:MaxGCPauseMillis=时间 #控制最大垃圾收集停顿时间单位ms,控制吞吐量
-XX:GCTimeRatio=比例 #GC时间占总时间的比率,默认值是99, 即允许1%的GC时间
-XX:+UseAdaptiveSizePolicy#动态调整新生代,老年代与 幸存区比例 等参数以获得最合适停顿时间或者最大吞吐量
Serial Old收集器
Serial Old是Serial收集器的老年代版本,是一个单线程收集器,使用标记-整理算法。供客户端模式下的HotSpot虚拟机使用。在服务端模式下,它也可能有两种用
途:一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用,另外一种就是作为CMS收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用。
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,是一个多线程并发收集器,基于标记-整理算法实现。
在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,只针对老年代进行垃圾回收,一般与ParNew 作为新生代垃圾收集器配合使用。基于标记-清除算法实现。一般应用于基于浏览器的B/S系统的服务端的Java应用。
整个过程分为四个步骤,包括:
1)初始标记(CMS initial mark)
2)并发标记(CMS concurrent mark)
3)重新标记(CMS remark)
4)并发清除(CMS concurrent sweep)
初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。通过上图可以比较清楚地看到CMS收集器的运作步骤中并发和需要停顿的阶段。
JVM参数:
-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld(见SerialOld介绍)
#gc线程数,一般是CPU核心数的四分之一
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
#当老年代达到多少空间之后进行CMS,默认68
-XX:CMSInitiatingOccupancyFraction=percent
#在重新标记之前先对新生代做一次垃圾回收
-XX:+CMSScavengeBeforeRemark
1.由于有一部分CPU核心需要用于垃圾回收,对整个应用的吞吐量有影响;
2.无法清理浮动垃圾,在下次清理时再处理;
3.需要预留空间保证能够容纳浮动垃圾,所以需要用上述参数来确定触发CMS的时机;
4.标记清除算法会产生大量空间碎片,导致无法容纳大对象,所以需要在发生full gc的时候进行内存碎片的合并整理,由-XX:+UseCMS-CompactAtFullCollection
控制默认开启。
Garbage First(G1)收集器
G1是一款面向服务端应用的垃圾收集器,期望未来能够替换掉CMS垃圾收集器。
适用场景:
- 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
- 超大堆内存,会将堆划分为多个大小相等的 Region
- 整体上是 标记+整理 算法,两个区域之间是 复制 算法
JVM参数:
XX:+UseG1GC
-XX:G1HeapRegionSize=size
-XX:MaxGCPauseMillis=time
它可以面向堆内存任何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。
G1开创的基于Region的堆内存布局是它能够实现这个目标的关键。G1仍遵循分代收集理论即保留新生代、老年代以及幸存区,但G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
G1垃圾回收阶段
G1有两种GC模式:Young GC(也可以叫Minor GC)和Mixed GC,两种都是完全Stop The World的。
- Young GC(Minor GC):选定所有年轻代里的Region。通过控制年轻代的region个数,即年轻代内存大小,来控制young GC的时间开销。
- Mixed GC:选定所有年轻代里的Region,外加根据global concurrent marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。
G1的Minor GC
通过标记-复制算法,将新生代的存活的对象转移到一个或者多个的survivor 块上,当达到一定代数,这部分对象会晋升至老年代。垃圾回收是会触发一次STW。
G1的Mixed GC
流程:
初始标记(Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
并发标记(Concurrent Marking):当老年代占用堆空间达到阈值时,进行并发标记,由-XX:InitiatingHeapOccupancyPercent
参数控制,默认是45%。从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。
最终标记(Final Marking):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
筛选回收(Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
ZGC
https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html