1.垃圾收集器的种类
如果说收集算法是垃圾回收的方法论,那么垃圾收集器是内存回收的具体实现
主要的垃圾收集器有以下几种:
新生代收集器
- Serial收集器
- ParNew收集器
- Parallel Scavenge收集器
老年代收集器
- Serial Old收集器
- Parallel Old收集器
- CMS收集器
- G1收集器
2.垃圾收集的时机
在分代模型的基础上,GC从时机上分为两种
- Scavenge GC—
Minor GC
- 触发时机:新对象生成时,Eden空间满了
理论上Eden区大多数对象会在Scavenge GC回收,复制算法的执行效率会很高,Scavenge GC的时间比较短
- 触发时机:新对象生成时,Eden空间满了
- Full GC
- 对这个JVM进行整理,包括Young, Old和Perm
- 主要的触发时机:
- Old满了
- Perm满了
- system.gc()
- 缺点
- 效率低----故尽量减少Full GC
3.垃圾收集器的并行和并发
- 并行
Parallel
—指多个收集器的线程同时工作,用户线程处于等待状态 - 并发
Concurrent
—指收集器在工作的同时,可以允许用户线程工作- 并发并不代表解决了GC停顿的问题,在关键的步骤还是要停顿
比如在收集器标记垃圾的时候
但在清除垃圾的时候,用户线程可以和GC线程并发执行
- 并发并不代表解决了GC停顿的问题,在关键的步骤还是要停顿
4.垃圾收集器
4.1 Serial收集器
4.1.1 简介
Serial收集器是单线程收集器
- 收集时会暂停所有工作线程(
Stop The World
—STW
) - 使用复制收集算法
- 虚拟机运行在Client模式时的默认新生代收集器
4.1.2特点
- 最早的收集器,单线程进行GC
- 新生代和老年代都可以使用
- 在新生代,采用复制算法
- 在老年代采用标记整理算法
- 单线程GC,没有多线程切换的额外开销,简单实用
- Hotspot Client模式默认的收集器
4.2 ParNew收集器
4.2.1简介
ParNew收集器就是Serial的多线程版本,除了使用多个收集线程外,其余行为包括
- 算法
- STW
- 对象分配规则
- 回收策略等
都与Serial收集器一模一样.
对应的这种收集器是虚拟机运行在Server模式的默认新生代收集器,在单CPU环境中,Parnew收集器并不会比Serial收集器有更好的效果
4.2.2 特点
- Serial收集器在新生代的多线程版本
- 使用复制算法
只有在多CPU的环境下,效率才会比在Serial收集器高
- 相关JVM参数
-XX:ParallelGCThreads
—控制GC线程数的多少,需要结合具体CPU的个数- Server模式下新生代的默认收集器
4.3.Parallel Scavenge收集器
Parallel Scavenge收集器也是一个多线程收集器
- 使用复制算法
- 其对象分配规则与回收策略都与ParNew收集器有所不同
- 它是以吞吐量最大化—GC时间占总运行时间最小为目标的收集器实现
- 它允许较长时间的STW换取总吞吐量最大化
4.4 Serial Old收集器
Serial Old收集器是单线程收集器
- 使用标记-整理算法那
- 是老年代的收集器
4.4 Parallel Old收集器
4.4.1 简介
老年代版本吞吐量优先收集器
- 使用多线程和标记-整理算法
- JVM 1.6提供
- 在此之前,新生代使用PS收集器的话,老年代出Serial Old收集器外别无选择—PS无法与CMS收集器配合工作
4.4.2 特点
- Parallel Scavenge在老年代的实现
- 在JVM1.6才出现该收集器
- 采用多线程标记-整理算法
- 更注重吞吐量
- Parallel Scavenge+Parallel Old = 高吞吐量,但GC停顿可能不理想
4.5 CMS收集器
4.5.1 简介
CMS(Concurrent Mark-Swap
)是一种以最短停顿时间为目标的收集器,使用CMS并不能达到GC效率最高—总体GC时间最小,但它尽可能降低GC时服务器的停顿时间
-
使用算法—标记-清除算法
-
目标—获取最短回收停顿时间为目标
多数应用于互联网或者B/S系统的服务器端上
4.5.2 执行步骤
整个过程大致分为四步
- 初始标记—initial mark
- 并发标记—Concurrent mark
- 重新标记—remark
- 并发清除—concurrent sweep
其中 - 初始标记,重新标记这两个步骤仍然需要STW
- 初始标记只是标记一下GC Roots能直接关联的对象,速度很快
- 并发标记阶段就是GC Roots Tracing的过程
- 重新标记阶段则是为了修正并发标记期间因用户线程继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段邵长一些,单元比并发标记的时间短
如下图所示
在整个过程中耗时最长的并发标记和并发清理过程收集器线程都可以与用户线程一起工作,因此,从总体上看,CMS收集器的内存回收过程是与用户线程一起并发执行的
CMS收集器实际为7步
- Initial Mark—初始标记 STW
- Concurrent Mark —并发标记
- Concurrent Preclean—并发预清除
- Concurrent Abortable Preclean—并发可失败的预清除
- Final Remark–最终重新标记 STW
- Concurrent Sweep—并发清除
- Concurrent Reset—并发重置
4.5.3 特点
- 追求最短停顿时间,非常适合Web应用
- 只针对老年区,一般结合ParNew使用
- Concurrent, GC线程和用户线程并发工作—尽可能并发
- Mark-Sweep
- 只有在多核心CPU环境下才有意义
- 相关JVM参数
-XX:+useConcMarkSweepGC
代开
4.5.4 优点
- 并发收集
- 低停顿
Oracle公司的一些官方文档也称之为并发低停顿收集器—Concurrent Low Pause Collector
4.5.5缺点
- CMS以牺牲CPU资源为代价来减少用户线程的停顿. 当CPU个数少于4的时候,有可能对吞吐量影响非常大
- CMS在并发清理的过程中,用户线程还在跑. 此时需要预留一部分空间给用户线程
- CMS用标记-清除算法会带来碎片问题.碎片过多的时候会容易频繁触发Full GC
- CMS收集器对CPU资源非常敏感
- CMS收集器无法处理
浮动垃圾Floating Garbage
可能出现"Concurrent Mode Failure"失败而导致另外一次Full GC的产生.
如果在应用中老年代增长不是很快,可以通过参数-XX:CMSInitiatingOccupancyFraction
的值来提高触发百分比,以便降低内存回收次数从而获取更好的性能
要是CMS运行期间预留的内存无法满足程序需要时,虚拟机将启动后备预案:临时启用Serial Old
收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了
所以说参数-XX:CMSInitiatingOccupancyFraction
设置的太高很容易导致大量的"Concurrent Mode Failure"失败
- 收集结束时会有大量空间碎片产品,空间碎片过多时,将会给大对象分配带来很大麻烦,往往出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前进行一次Full GC
CMS收集器提供了一个-XX:+UserCMSCompactAtFullCollection
开关参数(默认就是开启的)—用于在CMS收集器顶不住要进行Full GC时开启内存碎片的合并整理过程,内存整理过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长