前面一篇文章讲了垃圾回收的算法(传送门),今天来说一下从古到今一些经典的垃圾收集器,直接进入正题!
Serial 收集器也算上是一个老古董了,看到这个名字,我们就可以猜到这是一个单线程工作的收集器,采用的是标记-复制算法,这里的单线程不仅说它使用一个处理器或者一条线程,而是它必须暂停其他所有工作的线程,Stop The World 这个世界都暂停了,可想而知,你的电脑说不定在某个时刻就要暂停下来等待垃圾回收,这是多么糟糕的一件事情! 但是它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器,这时候问题来了 什么是客户端模式呢,是不是还有服务器模式呢 如果是64位的jdk,只能运行在Server模式下。而32位的jdk,默认是运行在client模式下,可以通过修改设置来指定默认的启动模式。 还可以通过在cmd命令行窗口用 java -version 查看
缺点就不用我多说了吧
ParNew 收集器实质上是Serial收集器的多线程版本,这里讲的多线程是多个垃圾收集的线程,采用的是标记-复制算法,并不是垃圾收集线程和用户线程并行,它也是用于新生代的收集器,相比Serial 并没有太多的创新,之后或说到一个CMS收集器,它的出现让ParNew 又活了一段时间,CMS作为老年代的收集器,ParNew 作为新生代的收集器。 但是好景不长,随着技术的日益发展,G1收集器的出现让ParNew彻底的退出了历史舞台(后面都会介绍)
Parallel Scavenge 收集器也是一款新生代的垃圾收集器,同样采用标记-复制算法,很多地方都和ParNew 收集器神似。但是它有它的独特之处! Parallel Scavenge 收集器目标是一个可控制的吞吐量,这时候问题来了 什么是吞吐量呢 吞吐量就是处理器运行代码的时间和处理器使用的总时间的比值,Parallel Scavenge 收集器提供了两个参数:
-XX:MaxGCPauseMillis 控制最大垃圾收集的停顿时间 参数值 是一个大于0的毫秒数,收集器在每次垃圾回收的时间不是超过这个值,不过大家不用以为把这个值调到很小,这样垃圾回收的效率就高了,这个想法是错误的,它牺牲了吞吐量和新生代的空间,比如说收集50M的空间肯定比收集100M的空间速度要快,之前10秒收集一次,每次需要100毫秒,现在3秒收集一次,每次耗时40毫秒,这会使垃圾回收的次数频繁,所以这个值一定要设置的合理-XX:GCTimeRatio 直接设置吞吐量的大小 参数值是大于0小于100的整数,比如我们设置19,允许垃圾回收的时间占总时间的5% (1/(1+19)),默认值是99 也就是垃圾回收的时间在总时间的1%Serial Old 收集器是Serial 老年代版本,同样也是单线程收集器, 使用标记-整理算法, 这个收集器和Serial 基本一样,这里就不做阐述,它与Parallel Scavenge收集器配合使用
Parallel Old 收集器,这个收集器说来也是尴尬,他是Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,也是基于标记-整理算法,在早期Parallel Scavenge 收集作为新生代的垃圾收集器,它只能选择Serial Old作为老年代的垃圾收集器,没有别的选择,但是由于单线程的老年代收集无法充分利用服务器多处理的并行处理能力,在服务器应用性能上的拖累,无法满足Parallel Scavenge 收集器吞吐量最大化! 所以Parallel Old 收集器的出现就是为了配合Parallel Scavenge 收集器,使得吞吐量最大化,也是名副其实的吞吐量优先组合
CMS 收集器是一款以获取最短回收停顿时间为目标的收集器,它是基于标记-清除算法实现的,它的运作过程很复杂,分为4步,下面具体的说一下每一步
初始标记 初始标记仍然需要Stop The World ,它仅仅是标记一下GC Roots能直接关联的对象,速度很快并发标记 从GC Roots对象 开始遍历整个对象图,这个过程用时很长,但是和用户线程一起并发执行,就是不需要用户等待重新标记 重新标记也需要Stop The World,修正在上一步并发标记的过程中,因为用户程序的运行导致一部分对象的引用发生了变化,这个阶段的时间也很短,并发清理 清理掉已经标记死亡的对象,这个过程也不需要用户等待,与用户线程并发执行篇幅有点长了后面的文章还会更新G1收集器、Shenandoah收集器、ZGC收集器等优秀的垃圾收集器 上面的几种垃圾收集器,你学废了吗?