目录
1 垃圾收集器的分类
1.1 次收集器
1.2 全收集器
1.3 垃圾回收器的常规匹配
2 常见垃圾回收算法
2.1 引用计数(Reference Counting)
2.2 复制(Copying)
2.3 标记-清除(Mark-Sweep)
2.4 标记-整理(Mark-Compact)
3 分代垃圾收集器
3.1 串行收集器(Serial)
3.2 并行收集器(ParNew)
3.3 Parallel Scavenge 收集器
Scavenge GC, 指发生在新生代的 GC,因为新生代的 Java 对象大多都是朝生夕死,所以Scavenge GC 非常频繁,一般回收速度也比较快。当 Eden 空间不足以为对象分配内存时,会触发 Scavenge GC。
一般情况下,当新对象生成,并且在 Eden 申请空间失败时,就会触发 Scavenge GC,对Eden 区域进行 GC,清除非存活对象,并且把尚且存活的对象移动到 Survivor 区。然后整理Survivor 的两个区。这种方式的 GC 是对年轻代的 Eden 区进行,不会影响到年老代。因为大部分对象都是从 Eden 区开始的,同时 Eden 区不会分配的很大,所以 Eden 区的 GC 会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使 Eden 去能尽快空闲出来。
当年轻代堆空间紧张时会被触发相对于全收集而言,收集间隔较短
Full GC,对整个堆进行回收(包括年轻代、老年代和持久代),出现了 Full GC 一般会伴随着至少一次的 Minor GC(老年代的对象大部分是 Scavenge GC 过程中从新生代进入老年代),比如:分配担保失败。 Full GC 的速度一般会比 Scavenge GC 慢 10 倍以上。当老年代内存不足或者显式调用 System.gc()方法时,会触发 Full GC。
当老年代或者持久代堆空间满了,会触发全收集操作可以使用 System.gc()方法来显式的启动全收集,全收集一般根据堆大小的不同,需要的时间不尽相同,但一般会比较长。
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题。
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小, 同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。 简图如下:
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。 简图如下:
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。 简图如下:
Serial 收集器是 Hotspot 运行在 Client 模式下的默认新生代收集器, 它的特点是: 只用一个 CPU(计算核心) /一条收集线程去完成 GC 工作, 且在进行垃圾收集时必须暂停其他所有的工作线程(“Stop The World” -后面简称 STW)。可以使用-XX:+UseSerialGC 打开。虽然是单线程收集, 但它却简单而高效, 在 VM 管理内存不大的情况下(收集几十 M~一两百 M 的新生代), 停顿时间完全可以控制在几十毫秒~一百多毫秒内。
ParNew 收集器其实是前面 Serial 的多线程版本, 除使用多条线程进行 GC 外, 包括 Serial可用的所有控制参数、收集算法、 STW、对象分配规则、回收策略等都与 Serial 完全一样(也是 VM 启用 CMS 收集器-XX: +UseConcMarkSweepGC 的默认新生代收集器)。由于存在线程切换的开销, ParNew 在单 CPU 的环境中比不上 Serial, 且在通过超线程技术实现的两个 CPU 的环境中也不能 100%保证能超越 Serial. 但随着可用的 CPU 数量的增加,收集效率肯定也会大大增加(ParNew 收集线程数与 CPU 的数量相同, 因此在 CPU 数量过大的环境中, 可用-XX:ParallelGCThreads=
与 ParNew 类似, Parallel Scavenge 也是使用复制算法, 也是并行多线程收集器. 但与其他收集器关注尽可能缩短垃圾收集时间不同, Parallel Scavenge 更关注系统吞吐量:系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)停顿时间越短就越适用于用户交互的程序-良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用 CPU时间,尽快地完成程序的运算任务. Parallel Scavenge 提供了如下参数设置系统吞吐量:
Parallel Scavenge 参数 | 描述 |
-XX:MaxGCPauseMillis | (毫秒数) 收集器将尽力保证内存回收花费的时间不超过 设定值, 但如果太小将会导致 GC 的频率增加. |
-XX:GCTimeRatio | (整数:0 < GCTimeRatio < 100) 是垃圾收集时间占总时间的 比率 |
XX:+UseAdaptiveSizePo licy |
启用 GC 自适应的调节策略: 不再需要手工指定-Xmn、 -XX:SurvivorRatio、 -XX:PretenureSizeThreshold 等细节参数, VM 会根据当前系统的运行情况收集性能监控信息, 动态调整这些 参数以提供最合适的停顿时间或最大的吞吐量 |