垃圾检测和收集算法只是内存回收的方法论,而垃圾收集器才是内存回收的具体实现(可理解为“接口”与“实现类”的关系)。
ps:GC垃圾回收(垃圾检测-回收算法):https://editor.csdn.net/md/?articleId=103819619
垃圾收集器分类:
新生代的收集器包括
- Serial
- PraNew
- Parallel Scavenge
老年代的收集器包括
- Serial Old
- Parallel Old
- CMS
回收整个Java堆(新生代和老年代)
- G1收集器
新生代垃圾收集器解析
Serial 收集器
Serial 收集器是最基础、历史最悠久的收集器。特点:
- 单线程收集,且垃圾收集时,必须暂停其他所有的工作线程(Stop The World, STW),直到它收集结束。
- HotSpot 虚拟机运行在 Client 模式下默认新生代收集器。
- 优于其他收集器的地方:简单而高效(与其他收集器的单线程比)。对于限定单个 CPU 的环境来说,Serial
收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
运行示意图如下:
ParNew 收集器
ParNew 收集器实质上是 Serial 收集器的多线程并行版本。
除了同时使用多条线程进行垃圾收集之外,其余的行为包括 Serial 收集器可用的所有控制参数(-XX:SurvivorRatio, -XX:PretenureSizeThreshold、-XX:HandlePromotionFailure 等)、收集算法、Stop The World、对象分配原则、回收策略等都与 Serial 收集器完全一致。
运行示意图:
使用/禁用该收集器的 VM 参数
注:JDK 9 取消了 -XX:+UseParNewGC 参数
-XX:+/-UseParNewGC
Parallel Scavenge 收集器
Parallel Scavenge 收集器是新生代收集器,也是使用标记-复制算法实现的、并行收集的多线程收集器,也称“吞吐量优先收集器”。
与 ParNew 类似,但关注点不同:
- CMS 等收集器:尽可能地缩短垃圾收集时用户线程的停顿时间;
- Parallel Scavenge 收集器:达到一个可控的吞吐量(Throughput)。
吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间),即运行用户代码时间所占比重。
响应速度快能提升用户体验;而吞吐量高则能更高效地利用 CPU 资源,尽快完成程序的计算任务(主要适合在后台运算而不需要太多交互的任务)。
运行示意图如下:
虚拟机参数
# 最大垃圾收集停顿时间(毫秒)
-XX:MaxGCPauseMillis
# 设置吞吐量(0~100)
-XX:GCTimeRatio
# 自适应调节策略
-XX:UseAdaptiveSizePolicy
老年代垃圾收集器解析
Serial Old 收集器
Serial 收集器的老年代版本,单线程,使用“标记-整理”算法。主要用于客户端模式下的 HotSpot 虚拟机。
运行示意图如下:
Parallel Old 收集器
Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,使用多线程和“标记-整理”算法实现。
运行示意图如下:
在注重吞吐量或者处理器资源较为稀缺的场合,都可以考虑 Parallel Scavenge + Parallel Old 收集器的组合。
CMS 收集器
CMS(Concurrent Mark Sweep)收集器是一种以「获取最短回收停顿时间」为目标的收集器。
它基于“标记-清除”算法实现,运作过程分为四步:
- 初步标记(CMS initial mark):只标记 GC Roots 能直接关联到的对象,速度很快;
- 并发标记(CMS concurrent mark):从 GC Roots
遍历整个对象图,耗时较长,但无需停顿用户线程(可与用户线程并发执行); - 重新标记(CMS remark):修正并发标记期间,因用户线程导致标记产生变动的标记记录;
- 并发清除(CMS concurrent sweep):清理删除标记阶段判断的已经死亡的对象,可与用户线程并发执行。
运行示意图如下:
目前很大一部分 Java 应用集中在互联网网站或者 B/S 系统的服务上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS 收集器非常符合这类应用的需求。
CMS 的主要优缺点
- 主要优点:并发收集、低停顿
- 主要缺点
- 对处理器资源非常敏感,降低吞吐量。
- 无法处理“浮动垃圾”,有可能出现“Concurrent Mode Failure”失败而导致另一次完全 Stop The World 的
Full GC 的产生。 - 内存空间碎片问题
浮动垃圾(Floating Garbage):在 CMS 的并发标记和并发清理阶段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程以后,CMS 无法在当次收集中处理掉它们,只好留待下一次垃圾收集时再清理掉。这部分垃圾就是“浮动垃圾”。
虚拟机参数
# 使用 CMS 收集器
-XX:+UseConcMarkSweepGC
# 老年代使用空间的比例(需根据实际情况权衡)
-XX:CMSInitiatingOccupancyFraction=80
# Full GC 时开启内存碎片的合并整理
-XX:+UseCMSCompactAtFullCollection
新生代和老年代垃圾收集器
G1 收集器
G1收集器-标记整理算法
JDK1.7后全新的回收器, 用于取代CMS收集器。
G1收集器的优势:
- 独特的分代垃圾回收器,分代GC: 分代收集器, 同时兼顾年轻代和老年代;
- 使用分区算法, 不要求eden, 年轻代或老年代的空间都连续;
- 并行性: 回收期间, 可由多个线程同时工作, 有效利用多核cpu资源;
- 空间整理: 回收过程中, 会进行适当对象移动, 减少空间碎片;
- 可预见性: G1可选取部分区域进行回收, 可以缩小回收范围, 减少全局停顿。
G1收集器的运作大致可划分为一下步骤:
G1收集器的阶段分以下几个步骤:
- 初始标记(它标记了从GC Root开始直接可达的对象);
- 并发标记(从GC Roots开始对堆中对象进行可达性分析,找出存活对象);
- 最终标记(标记那些在并发标记阶段发生变化的对象,将被回收);
- 筛选回收(首先对各个Regin的回收价值和成本进行排序,根据用户所期待的GC
- 停顿时间指定回收计划,回收一部分Region)。
查看jdk1.8x默认垃圾收集器
方式一:通过java -XX:+PrintCommandLineFlags -version命令查看
C:\Users\张江丰>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=132392832 -XX:MaxHeapSize=2118285312 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
方式二:通过jconsole查看
注:PS MarkSweep(Serial Old) PS Scavenge(Parallel Scavenge)
在JVM中是+XX配置实现的搭配组合:
介绍了JVM中的垃圾回收器,主要包括串行回收器、并行回收器以及CMS回收器、G1回收器。他们各自都有优缺点,通常来说你需要根据你的业务,进行基于垃圾回收器的性能测试,然后再做选择。下面给出配置回收器时,经常使用的参数:
- UseSerialGC 虚拟机运行在Client模式下的默认值,表示用 Serial + Serial Old 的收集器组合进行内存回收
- UseParNewGC 表示用 ParNew + Serial Old 的收集器组合进行内存回收
- UseConcMarkSweepGC 表示用 “ParNew” + “CMS” + "Serial Old"的收集器组合,Serial Old 收集器作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器
- UseParallelGC 虚拟机运行在Server模式下的默认值,表示用 Parallel Scavenge + Serial Old的收集器组合进行内存回收
- UseParallelOldGC 表示用 Parallel Scavenge + Parallel Old的收集器组合进行内存回收
参考资料:
《深入理解Java虚拟机》