垃圾收集算法:为实现垃圾回收提供理论支持
垃圾收集器:利用垃圾收集算法,实现垃圾回收的实践落地。
各种垃圾收集器之间也是可以配合使用的:
比如 CMS 可以和ParNew配合,CMS 和 Parallele Scavenge 不能使用。
下图中表示了各个垃圾收集器作用的范围,以及各个垃圾收集器之间可以配合使用的关系
Stop The World
简写为 STW,也叫全局停顿,Java代码停止运行,native 代码继续运行,但不能与JVM进行交互。 原因:多半由于垃圾回收导致;也可能是 Dump线程,死锁检查、 Dump 堆等导致。Stop The World 会导致服务停止,请求没有响应;主从切换,危害生产环境
并行收集 vs 并发收集
并行收集:指多个垃圾收集线程并行工作,但是收集过程中,用户线程(你的业务线程)还是处于等待状态。
并发收集:指用户线程与垃圾收集线程同时工作
吞吐量
- 吞吐量是指CPU用于运行用户代码的时间与CPU总消耗时间的比值。
- 计算公式:运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。比如Jvm已经运行了80分钟,垃圾收集总共花费了1分钟,也就是 80/81
垃圾收集器
1. Serial 收集器
采用复制算法。是新生代的垃圾收集器。
- 是单线程的收集器。
- 收集过程全程 Stop The World。
- 适用于客户端程序,以 -client运行,默认就是使用 Serial。
- 适用于单核机器
2. ParNew 收集器
Serial 收集器德多线程版本,除使用了多线程外,其余和 Serial收集器一样。包括:JVM参数,Stop The World 的表现,垃圾收集算法都是一样的。
- 多线程
- 可以使用 -XX:ParallelGCThreads 设置垃圾收集的线程数。一般设置成同CPU核心数就可以了。
- 主要用来和 CMS 配合使用
3. Parallele Scavenge
Parallele Scavenge 收集器 关注的是吞吐量。适用于注重吞吐量的场景
- 也叫做吞吐量优先收集器
- 采用复制算法
- 也是并行的多线程收集器,这一点和 ParNew类似
- Parallele Scavenge 的特点 :
- 可以达到一个可控制的吞吐量
- -XX:MaxGCPauseMillis : 控制最大的垃圾收集停顿时间(尽力)。比如设置100ms,垃圾收集每次时间尽量不超过100ms
- -XX:GCTimeRatis : 设置吞吐量的大小,取值0-100,系统花费不超过 1/(1+n) 的时间用于垃圾收集
- 自适应垃圾收集策略 : 可用 -XX:+UseAdptiveSizePolicy 打开。
- 打开自适应策略后,无需手动设置新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)等参数。虚拟机会自动根据系统的运行状况收集性能监控信息,动态的调整这些参数,从而达到最优的停顿时间以及最高的吞吐量
- 可以达到一个可控制的吞吐量
4. 老年代收集器 Serial Old 收集器
Serial Old 收集器 是 Serial 的老年代版本。采用标记-整理算法。适用场景:
- 可以和Serial 、ParNew 、Parallel Scavenge 这三个新生代的垃圾收集器配合使用
- CMS收集器出现故障的时候,会用 Serial Old 作为后备
5. 老年代收集器 Parallel Old 收集器
Parallel Old 是 Parallele Scavenge 的老年代版本。采用 标记-整理算法。
- 只能和 Parallele Scavenge 配合使用。所以适用场景也是关注吞吐量的场景
6. 老年代收集器 CMS 收集器
CMS :Concurrent Mark Sweep 。 并发收集器。使用 标记-清除算法。上文讲的都是串行、并行收集器。
1. 初始标记
- 标记GC Roots能直接关联到的对象,会 Stop The World
2. 并发标记 (concurrent mark)
- 找出所有GC Roots 能关联到的对象。并发执行,无 Stop The World
3. 并发预清理(concurrent-preclean)此阶段可能会执行可能不会执行
- 重新标记那些在并发标记阶段,引用被更新的对象,从而减少后面重新标记阶段的工作量。无 Stop The World
- 可使用 XX:-CMSPrecleaningEnabled 关闭并发预清理阶段,默认打开
4. 并发可中止的预清理阶段(concurrent-abortable-preclean) 经过并发预清理之后,又会经过一个可能会执行可能不会执行的阶段
- 和并发预清理做的事情一样,此阶段并发执行,无 Stop The World
- 当Eden的使用量大于CMSScheduleRemarkEdenSizeThreshold 的阈值(默认2M)时,才能执行该阶段
- 并发可中止的预清理阶段的主要作用是:允许我们能够控制预清理阶段的结束时机。比如扫描多长时间(CMSMaxAbortablePrecleanTime,默认5秒)或者 Eden 区使用占比达到一定的阈值(CMSScheduleRemarkEdenPenetration,默认50%)就结束本阶段
5. 重新标记(remark)
- 修正并发标记期间,因为用户程序继续执行,导致标记发生变动的那些对象的标记
- 一般来说,重新标记花费的时间会比初始标记阶段长一些,但比并发标记的时间短。
- 此阶段 会 Stop The World
6. 并发清理(concurrent sweep)
- 基于标记结果,清除掉前面标记出来的垃圾。此阶段并发执行, 无 Stop The World
7. 并发重置(concurrent reset)
- 清理本次CMS GC的上下文信息,为下一次GC做准备
CMS 收集器 优点
- Stop The World 时间短
- 大多过程并发执行
CMS 收集器 缺点
- CPU资源比较敏感,并发阶段可能导致应用吞吐量的降低
- 无法处理浮动垃圾。并发清理阶段业务还在运行,会产生新的垃圾,这部分的叫浮动垃圾
- 不能等到老年代几乎满了才开始收集。预留的内存不够 →引发了“Concurrent Mode Failure”问题 →使用“Serial Old”垃圾回收器作为后备
- 可使用 CMSInitiatingOccupancyFraction 设置老年代占比达到多少就触发垃圾收集器,默认是68%
- 内存碎片的问题。因为是使用标记-清除。
- XX:+UseCMSCompactAtFullCollection”,默认就打开了。意思是在Full GC之后要再次进行“Stop the World”,停止工作线程,然后进行碎片整理,就是把存活对象挪到一起,空出来大片连续内存空间,避免内存碎片。
- -XX:CMSFullGCsBeforeCompaction”,这个意思是执行多少次Full GC之后再执行一次内存碎片整理的工作,默认是0,意思就是每次Full GC之后都会进行一次内存整理。
CMS 收集器适用场景:希望体统停顿时间短,响应速度快的场景,比如各种服务器应用程序
G1 收集器
G1 收集器 全称 Garbge First , 是面向服务器端的应用的垃圾收集器。G1 把内存划分为大小相等的 Region 。
通过参数:-XX:G1HeapRegionSize 指定Region的大小,取值范围 1 ~ 32 MB。一定要是2的N次幂。把大对象(超过 Regiony一半大小)放在 Humongous 中
它既可以用在新生代,也可以用在老年代。
G1 收集器 设计思想
把内存区域分为若干个Region,然后去跟踪每一个 Region里面垃圾堆积的价值大小,若回收掉这个Region,能够获得多少剩余的空间,之后G1收集器会在后台构建一个优先列表,根据上面的价值大小,做了个排序。同时会根据设置的允许的收集时间,去优先回收价值高的Region。这样就可以获得一个更高的垃圾回收效率。这就是前文提到的增量算法的思想
G1 收集器 - 垃圾收集机制
G1提供了三种垃圾垃圾机制:
- Young GC
- Mixed GC
- Full GC
Young GC
- 当所有Eden Region 都满了的时候,就会触发Young GC
- Eden 里面的存活对象会转移到 Survivor Region 里面去
- 原先 Survivor Region 中的存活对象转移到新的 Survivor Region中,或者(15次)晋升到 Old Region
- 空闲的Region会被放入空闲列表中,等待下次被使用
Mixed GC
Mixed GC 是G1最能体现设计思想的地方。
- 当老年代大小占整个堆的百分比达到一定阈值(可用 -XX:InitiatingHeapOccupancyPercent指定,默认45%),就触发Mixed GC
- Mixed GC 会回收所有 Young Region , 同时回收部分 Old Region。要回收的这部分 Old Region 是根据设置的允许的收集时间,前面讲的回收价值高的Region 去选择
Mixed GC 执行过程
1. 初始标记
- 标记GC Roots能直接关联到的对象,存在 Stop The World 。和CMS类似
2.并发标记 (concurrent mark)
- 同CMS的并发标记,并发执行,没有stw
3. 最终标记(Final Marking)
- 修正在并发标记期间引起的变动,存在 Stop The World
4. 筛选回收(Live Data Counting and Evacuation)
- 对各个Region的回收价值和成本进行排序
- 根据用户所期望的停顿时间(MaxGCPauseMillis)来制定回收计划,并选择一些Region回收。存在 Stop The World
- 回收过程:选择一系列Region构成一个回收集(放到一个set集合里),把决定回收的Region中的存活对象复制到空的Region中,删除掉需要回收的Region ->无内存碎片
Full GC
- 复制对象内存不够,或者无法分配足够的内存(比如巨型对象没有足够的连续区分分配)时,会触发Full GC。Full GC 使用 Serial Old 模式。会长时间的 STW.
- G1 优化原则:尽量减少Full GC 的发生
- 增加预留内存(增大-XX:G1ReservePercent,默认为堆的10%)
- 更早的回收垃圾(减少 -XX:InitiatingHeapOccupancyPercent,老年代达到该值就触发 Mixed GC,默认是45%)。就是尽量让GC发生在 Young GC、Mixed GC
其它垃圾收集器
- Shenandoah
- ZGC
- Epsilon
如何选择垃圾收集器
- 应用系统关注的主要矛盾点是什么(吞吐量? 低延迟?)
- 基础设施(服务器硬件、系统)
- jdk版本