上一篇总结了GC的收集算法原理,接下来接着总结几种垃圾收集器和GC回收策略。
基于《深入理解Java虚拟机 第三版》第三章总结
目录
经典垃圾收集器
- Serial收集器
- ParNew收集器
- Parallel Scavenge收集器
- Serial Old收集器
- Parallel Old收集器
- CMS收集器
- Garbage First收集器
Serial收集器
- 最基础、历史最悠久的收集器,曾经(在JDK 1.3.1之前)是HotSpot虚拟机新生代收集器的唯一选择
- 单线程 :只会使用一个处理器或一条收集线程去完成垃圾收集工作。
- STW:进行垃圾收集时, 必须暂停其他所有工作线程, 直到它收集结束。
- 优点:简单而高效(与其他收集器的单线程相比) ,适用于运行在客户端模式下的虚拟机(线程停顿时间在100毫秒内且不频繁gc)
ParNew收集器
Serial收集器的多线程并行版本
JDK 7之前的遗留系统中首选的新生代收集器,因为除了Serial收集器外,目前只有它能与CMS收集器配合工作
除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致
单核处理器的环境中比Serial收集器差
多核处理器的环境中默认开启的收集线程数与处理器核心数量相同,在处理器核心非常多的环境中,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。
Parallel Scavenge收集器
新生代收集器,它同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器
特点是它的关注点与其他收集器不同:目标是达到一个可控制的吞吐量(Throughput)
- 例子:虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
- 停顿时间越短就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验;而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
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)收集器是一种基于标记-清除算法实现,以获取最短回收停顿时间为目标的收集器。
适用场景:较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验的应用,如运行在互联网网站或者基于浏览器的B/S系统的服务端上的Java应用
优点:并发收集、 低停顿
缺点:
- 1、会占用一部分线程资源,导致应用程序变慢, 降低总吞吐量
- 2、无法处理“浮动垃圾”,有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生
- 3、基于“标记-清除”算法实现,收集结束时会有大量空间碎片产生。
主要步骤:
- 初始标记 仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;
- 并发标记 从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;
- 重新标记 为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录(详见3.4.6节中关于增量更新的讲解),这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;
- 并发清除 清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
Garbage First收集器
主要面向服务端应用的垃圾收集器
在G1收集器出现之前的所有其他收集器,包括CMS在内,垃圾收集的目标范围要么是整个新生代(Minor GC) ,要么就是整个老年代(Major GC),再要么就是整个Java堆(Full GC)。
而G1收集器可以面向堆内存任何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。
G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值收益最大的那些Region,这也就是“Garbage First”名字的由来。这种使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限的时间内获取尽可能高的收集效率。
可以由用户指定期望的停顿时间。
G1运作期间不会产生内存空间碎片,垃圾收集完成之后能提供规整的可用内存。这种特性有利于程序长时间运行,在程序为大对象分配内存时不容易因无法找到连续内存空间而提前触发下一次收集。
内存分配与回收策略
- 对象优先在Eden分配
- 大对象直接进入老年代
- 长期存活的对象将进入老年代
- 动态对象年龄判定
- 空间分配担保
垃圾收集器参数总结
- 见ppt
java中的内存泄漏问题
- 见ppt
引用和finalize()
- 见ppt
