您的位置:首页 > 编程语言 > Java开发

java虚拟机学习之(七)常见垃圾收集器

2018-03-18 17:08 281 查看
在此之前我们已经介绍了Hotspot虚拟机如何去发起内存回收,介绍了如何判断对象是否存活,介绍了垃圾回收的各种算法,对比了这些算法 的优缺点,但是虚拟机如何具体的进行垃圾回收动作仍然未涉及,因为内存回收如何进行是由虚拟机所选用的GC收集器而决定的,而通常虚拟机中往往不止一种GC收集器。下面我们来对一些常用的垃圾收集器进行对比介绍。
1.Serial收集器Serial收集器是最基本、发展历史最悠久的收集器,在jdk1.3之前是虚拟机新生代收集的唯一选择。该收集器是一个单线程的收集器,其在进行垃圾收集时,必须暂停其它所有的工作线程,直至它收集结束。
优点:与其它收集器的单线程相比,简单高效,对于限定单个CPU环境来说,Serial收集器由于没有线程交互的开销,自然收集效率很高。适用场景:桌面应用,运行在client模式下的虚拟机中。
2.ParNew收集器ParNew收集器其实就是Serial收集器的多线程版本。与Serial收集器相比,除了多线程收集之外,没有太多的创新之处,但它却是很多运行在Server模式下的虚拟机中首选的新生代收集器。
3.parallel Scavenge收集器Parallel Scavenge收集器是一个新生代收集器,也叫“吞吐量优先收集器”,使用复制算法同时也是并行的多线程收集器。该收集器的特点是,其关注点与其它收集器不同,CMS等收集器关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控的吞吐量(即CPU用于运行用户代码的时间与总消耗时间的比值)。
适用场景:适用于在后台运算而不需要太多交互的任务。
4.Serial Old收集器Serial Old收集器是Serial收集器的老年代版本,它也属于单线程收集器,使用“标记—整理”算法。该收集器主要是给Client模式下的虚拟机使用。在Server模式下,该收集器还有两大用途:1)在jdk1.5以及之前的版本中与Parallel Scavenge收集器搭配使用;2)作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。
5.Parallel Old收集器Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记—整理”算法。该收集器在jdk1.6之后才开始提供。在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的境地,原因是新生代选择了Parallel Scavenge收集器,老年代除了Serial Old收集器外别无选择。由于老年代Serial Old收集器在服务端应用性能上的“拖累”,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果。该收集器出现后,“吞吐量优先收集器”终于有了比较可靠的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
6.CMS收集器CMS(Concurrent Mark Sweep)收集器是一种以获得最短回收停顿时间为目标的收集器。从名字上就可以知道,CMS收集器也是使用“标记—清除”算法实现的,希望系统停顿时间最短,以给用户带来较好的体验该收集器的运作过程较前几种收集器老说更复杂一些,整个过程分为四个步骤:
1)初始标记(CMS initial mark)
2)并发标记(CMS concurrent mark)
3)重新标记(CMS remark)
4)并发清除(CMS concurrent sweep)
其中,初始标记和重新标记这两个步骤仍然需要“Stop The World”。初始标记仅仅是标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing的过程;而重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长,但远比并发标记的时间要短。
优点:并发收集、低停顿
缺点:1)对CPU资源敏感;2)无法处理浮动来及(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生;3)该收集器使用“标记—清除”算法实现,这就意味着收集结束时会产生大量空间碎片。
7.G1收集器G1(Garbage 1)收集器是当今收集器技术发展的最前沿成果之一,它是一款面向服务端应用的垃圾收集器。与其它垃圾收集器相比,G1收集器具有以下特点:
1)并行与并发:G1能够充分利用多CPU,多核环境下的硬件优势,使多个CPU来缩短Stop-The-World停顿的时间,部分其它收集器原本需要停顿java线程执行的GC动作,GC收集器仍然可以通过并发的方式让java程序继续执行。
2)分代收集:与其它收集器一样,分代概念在G1中得以保留。虽然G1可以不需要其它收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已存活一段时间、熬过多次GC的旧对象以获取更好的收集效果。
3)空间整合:与CMS的“标记—清理”算法不同,G1从整体上来看是基于“标记—整理”算法实现的收集器,从局部(两个Region)来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存这种特性有利于程序长期运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
4)可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了降低停顿时间外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个程度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不得超过N毫秒。
在G1收集器之前的其它收集器的垃圾收集范围都是整个新生代或者老年代,而G1将整个Java堆划分为多个大小相等的独立区域,虽然还保留着新生代和老年代的概念,但新生代和老年代不再是物理隔离的,它们都是一部分Region的集合。
G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划的避免整个java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所得空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先收回价值最大的Region。
G1把内存“化整为零”的思路理解起来很容易,但其中的实现细节远没有想象的那么简单。Region不可能是孤立的,一个对象在某个Region中完成分配,并不意味着引用该对象的其它对象也在该Region中,而是整个java堆中的其它对象都有可能与该对象发生引用关系。那在做可达性分析判定对象是否还存活的时候,岂不得扫描整个java堆才能保证准确的性?在G1收集器中,Region之间的对象引用以及其它收集器中对新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处于不同的Region之中,如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描,也不会有遗漏。
如果不计算维护Remembered Set的操作,G1
4000
收集器的运作大致可分为以下几个步骤:
1)初始标记(Initial Marking)
2)并发标记(Concurrent Marking)
3)最终标记(Final Marking)
4)筛选回收(Live Data Counting and Evacuation)
初始标记阶段仅仅是标记一下GC roots能直接关联的对象,并且修改TAMS(Next Top at mark Start)的值,让下一阶段程序并发运行时,能在正确可用的Region中创建新对象,这个阶段需要停顿线程,但是耗时很短。并发标记阶段是从GC roots开始对堆中的对象进行可达性分析,找出存活的对象,这个阶段耗时较长,但是可以与用户程序并发执行。最终标记阶段是为了修正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs中,最终标记阶段需要把remembered Set Logs的数据合并到Remembered Set中,这个阶段需要停顿线程,但是可并行执行。最后字筛选回收阶段,首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划完成回收。
总结:
用于年轻代收集:Serial(单线程),ParNew(多线程,可看做Serial的多线程版本),Parallel Scavenge(吞吐量优先,多线程)
用于年老代收集:Serial Old(单线程,标记—整理算法,能与Serial,ParNew,Parallel Scavenge配合使用),Parallel Old(Parallel Scavenge的老年代版本,多线程,标记—整理算法,只能与Parallel Scavenge搭配使用),CMS(多线程,标记—清除算法,会产生大量空间碎片,能与Serial,ParNew配合使用,为预防“Concurrent Mode Failure”出现,需Serial Old作为失败后的候选)
G1(多线程,划分java堆为多个Region,宏观上采用标记—整理算法,局部来看是复制算法,不需要与其它收集器配合使用)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: