垃圾收集器比较和收集算法

来源:互联网 发布:犀牛软件多大 编辑:程序博客网 时间:2024/06/06 02:46

Java有四种类型的垃圾回收器

串行垃圾回收器(Serial Garbage Collector)
并行垃圾回收器(Parallel Garbage Collector)
并发标记扫描垃圾回收器(CMS Garbage Collector)
G1垃圾回收器(G1 Garbage Collector)

串行回收器(Serial Collector)
单线程执行回收操作,回收期间暂停所有应用线程的执行,client模式下的默认回收器,通过-XX:+UseSerialGC命令行可选项强制指定。
• 年轻代的回收算法(Minor Collection)
把Eden区的存活对象移到To区,To区装不下直接移到年老代,把From区的移到To区,To区装不下直接移到年老代,From区里面年龄很大的升级到年老代。 回收结束之后,Eden和From区都为空,此时把From和To的功能互换,From变To,To变From,每一轮回收之前To都是空的。设计的选型为复制。
• 年老代的回收算法(Full Collection)
年老代的回收分为三个步骤,标记(Mark)、清除(Sweep)、合并(Compact)。标记阶段把所有存活的对象标记出来,清除阶段释放所有死亡的对象,合并阶段 把所有活着的对象合并到年老代的前部分,把空闲的片段都留到后面。设计的选型为合并,减少内存的碎片。

并行回收器(Parallel Collector)
使用多个线程同时进行垃圾回收,多核环境里面可以充分的利用CPU资源,减少回收时间,增加JVM生产率,Server模式下的默认回收器。与串行回收器相同,回收期间暂停所有应用线程的执行。通过-XX:+UseParallelGC命令行可选项强制指定。
• 年轻代的回收算法(Minor Collection)
使用多个线程回收垃圾,每一个线程的算法与串行回收器相同。
• 年老代的回收算法(Full Collection)
年老代依然是单线程的,与串行回收器相同。

并行合并收集器(Parallel Compacting Collection)
年轻代和年老代的回收都是用多线程处理。通过命令可选项-XX:+UseParallelOldGC指定,–XX:ParallelGCThreads=3还可进一步指定参与并行回收的线程数。与串行回收器相同,回收期间暂停所有应用线程的执行。与并行回收器相比,年老代的回收时间更短,从而减少了暂停时间间隔(Pause time)。通过–XX:+UseParallelOldGC命令行可选项强制指定。
• 年轻代的回收算法(Minor Collection)
与并行回收器(Parallel Collector)相同
• 年老代的回收算法(Full Collection)
年老代分为三个步骤,标记、统计、合并。这里用到分的思想,把年老代划分为很多个固定大小的区(region)。 标记阶段,把所有存活的对象划分为N组(应该与回收线程数相同),每一个线程独立的负责自己那一组,标记存活对象的位置以及 所在区(Region)的存活率信息,标记为并行的。统计阶段,统计每一个区(Region)的存活率,原则上靠前面的存活率较高,从前到后, 找到值得合并的开始位置(绝大多数对象都存活的区不值得合并),统计阶段是串行的(单线程)。合并阶段,依据统计阶段的信息,多线程 并行的把存活的对象从一个区(Region)复制到另外一个区(Region)。

并发标记清除回收器(Concurrent Mark-Sweep Collector)
又名低延时收集器(Low-latency Collector),通过各种手段使得应用程序被挂起的时间最短。基本与应用程序并发地执行回收操作,没有合并和复制操作。通过命令行-XX:+UseConcMarkSweepGC指定,在单核或者双核系统里面还可以指定使用增量式回收模式-XX:+UseConcMarkSweepGC。增量式回收是指把回收操作分为多个片段,执行一个片段之后释放CPU资源给应用程序,未来的某个时点接着上次的结果继续回收下去。目的也是减少延时。
• 年轻代的回收算法(Minor Collection)
与并行回收器(Parallel Collector)相同
• 年老代的回收算法(Full Collection)
分为四个步骤,初始标记(Initial Mark)、并发标记(Concurrent Mark)、再次标记(Remark)、以及并发清理(Concurrent Sweep)。特别注意,没有合并操作,所以会有碎片。
• 初始化阶段: 暂停应用线程,找出所有存活的对象,耗时比较短,回收器使用单线程。
• 并发标记阶段: 回收器标记操作与应用并发运行,回收器使用单线程标记存活对象。
• 再次标记:并发标记阶段由于应用程序也在运行,这个过程中可能新增或者修改对象。所以再次暂停应用线程,找出所有修改的对象,使用多线程标记。
• 并发清理:回收器清理操作与应用并发运行,回收器使用单线程清理死亡对象。

JVM垃圾收集器种类及特点,优缺点

1、Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器,在JDK1.3.1之前是新生代搜集的唯一选择。

优点:简单高效、对于限定单个cpu的环境来说,serial收集器没有线程交互的开销,可专心做垃圾收集以获得最高的单线程收集效率。
缺点:单线程收集器,在进行垃圾收集时,必须暂停其他所有的工作线程,直到它搜集结束。
适用场景:用户桌面应用(一般用户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大,收集几十兆或者一两百兆的新生代),停顿时间可以控制在100毫秒左右,这点停顿是可以接受的。
2、ParNew收集器
ParNew收集器其实是Serial收集器的多线程版本。除了多线程收集,其他与Serial收集器相比并没有太多创新之处,但它却是许多运行在Server模式下的虚拟机中首选的收集器,其中的一个与性能无关的原因是,除了Serial收集器,目前只有它能和CMS收集器配合工作(CMS收集器是hotspot虚拟机中第一款真正意义上的并发收集器,简单来说就是你可以一边制造垃圾,收集器在不打断你的情况下进行收集)。
ParNew收集器适用于多核cpu环境,在单核情况下性能往往不如Serial收集器,甚至优于存在线程交互的开销,该收集器在通过超线程技术实现的两个cpu的环境中都不能百分之百的保证可以超越Serial收集器。不过一般在服务器环境下,多核心处理器非常常见,开发者可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

3、Parallel Scavenge收集器
与其收集器关注点不同的是,Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput),吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
适用场景:后台运算而不需要太多交互的任务。
特点:Parallel Scavenge收集器有一个参数-XX:+UseAdaptiveSizePolicy值得关注,这是一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小(-Xmn),eden和Surivior区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:pretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略。
4、Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。
使用场景:
1)Client模式。
2)Server端,JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用。
3)Server端,CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
运行示意图如下:
5、Parallel Old收集器
这个收集器是JDK1.6中开始提供的,在此之前跟Parallel Scavenge相对应的是老生代收集器是PS MarkSweep收集器,这个收集器本身与Serial Old非常接近,所以在官方的许多质量中都是直接以Serial Old代替PS MarkSweep进行讲解。
由于Serial Old收集器是单线程,所以其在服务端应用性能会拖累Parallel Scavenge,也就是说是用了Parallel Scavenge也不一定获得吞吐量最大化的效果。
直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old收集器组合,这两个组合工作过程如下:

6、CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以最短回收停顿时间为目标的收集器。目前很大一部分JAVA应用集中在互联网网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的用户体验,而CMS收集器就非常符合这类应用的需求。
从名字(Mark Sweep)上就可看出,CMS收集器是基于“标记-清除”算法实现的,它的运作过程分为4个步骤,包括:初始标识、并发标识、重新标识、并发清除。其中初始标识和重新标识这两个步骤仍然需要“Stop the world”。初始标记仅仅只是标记一下GCRoots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。
由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

优点:并发收集、低停顿。
缺点:1、CPU资源敏感。
2、无法处理浮动垃圾(Floating Garbage),即无法收集并发运行中产生的新的垃圾。
3、容易产生空间碎片。不过为了解决这个问题,CMS收集器提供了一个+UserCmsCompactAtFullCollection开关参数(默认开启),用于在CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理工作,这个过程是无法并发的,所以每隔一段时间就会出现停顿时间稍长的问题。此外,还有另外一个参数+XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩的FullGC后,跟着来一次压缩的(默认值为0,即每次进入Full GC时都进行碎片整理)。、
7、G1收集器(了解)
G1(Garbage-First)是目前最前沿的成果之一。是面向服务端应用的垃圾收集器,hotspot团队赋予它的使命是在未来替换掉JDK1.5中发布的CMS收集器。在JDK7u4(即JDK Update4)及以后的版本中G1收集器可以进行商用了。
优点:并行与并发、分代收集、空间整合、可预测的停顿

垃圾回收算法优缺点


可作为GC Roots 的对象包括:
虚拟机栈(栈帧中的本地变量表)中的引用对象。
方法区中的类静态属性引用的对象
方法区中的常量引用的对象
本地方法栈中JNI的引用对象。

主要有复制、标记清除、标记压缩三种实现算法。
1. 标记 - 清除算法
标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。
它的主要缺点:
①.标记和清除过程效率不高
②.标记清除之后会产生大量不连续的内存碎片。

  1. 复制算法
    它将可用内存容量划分为大小相等的两块,每次只使用其中的一块。当这一块用完之后,就将还存活的对象复制到另外一块上面,然后在把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,不会产生碎片等情况,只要移动堆订的指针,按顺序分配内存即可,实现简单,运行高效。
    主要缺点:
    内存缩小为原来的一半。

  2. 标记 - 整理算法
    标记操作和“标记-清除”算法一致,后续操作不只是直接清理对象,而是在清理无用对象完成后让所有存活的对象都向一端移动,并更新引用其对象的指针。
    主要缺点:
    在标记-清除的基础上还需进行对象的移动,成本相对较高,好处则是不会产生内存碎片。

1、Serial收集器曾经是虚拟机新生代收集的唯一选择,是一个单线程的收集器,在进行收集垃圾时,必须stop the world,它是虚拟机运行在Client模式下的默认新生代收集器。
2、Serial Old是Serial收集器的老年代版本,同样是单线程收集器,使用标记整理算法。
3、ParNew收集器是Serial收集器的多线程版本,许多运行在Server模式下的虚拟机中首选的新生代收集器,除Serial外,只有它能与CMS收集器配合工作。
4、Parallel Scavenge收集器也是新生代收集器,使用复制算法又是并行的多线程收集器,它的目标是达到一个可控制的运行用户代码跟(运行用户代码+垃圾收集时间)的百分比值。
5、Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记整理算法。
6、Concurrent Mark Sweep 收集器是一种以获得最短回收停顿时间为目标的收集器,基于标记清除算法。
过程如下:初始标记,并发标记,重新标记,并发清除,优点是并发收集,低停顿,缺点是对CPU资源非常敏感,无法处理浮动垃圾,收集结束会产生大量空间碎片。
7、G1收集器是基于标记整理算法实现的,不会产生空间碎片,可以精确地控制停顿,将堆划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)。

原创粉丝点击