前言
如果说垃圾回收算法是内存回收的方法论,那么垃圾收集器就是具体实现。jvm会结合针对不同的场景及用户的配置使用不同的收集器。
我把GC垃圾收集器分为三类:
一、新生代收集器
1、Serial
概念:虽然Serial看起来很坑,需停掉别的线程以完成自己的gc工作,但是也不是完全没用的,比如说Serial在运行在Client模式下优于其它收集器[简单高效,不过一般都是用Server模式,64bit的jvm甚至没Client模式]。
垃圾收集算法:采用复制算法,他是最基本、发展最久的收集器;
Serial收集器只能使用一条线程进行收集工作,在收集的时候必须得停掉其它线程,等待收集工作完成其它线程才可以继续工作;
Serial收集器的优点是对于Client模式下的jvm来说是个好的选择。适用于单核CPU【现在基本都是多核了】;
Serial收集器的缺点是收集时要暂停其它线程,有点浪费资源,多核下显得。
2.ParNew
ParNew默认开启线程数和当前cpu数量相同【几核就是几个,超线程cpu的话就不清楚了 - -】,如果cpu核数很多不想用那么多,可以通过-XX:ParallelGCThreads来控制垃圾收集线程的数量
垃圾收集算法:采用复制算法,它是Serial的升级版本,除了使用多线程外,其他的跟Serial是一样的,他是HotSpot第一个真正意义上实现并发的收集器【支持多线程-GC线程】;
ParNew收集器的归纳为两个优点: 第一、它支持多线程,多核CPU下可以充分的利用CPU资源; 第二、运行下Server模式下新生代首选的收集器,主要原因是新生代的这几个收集器只有它和Serial可以配合CMS收集器一起使用;
ParNew收集器的缺点:它在单核模式下表现不会比Serial好,由于在单核中利用了多核的优势,在线程收集过程中可能会出现频繁上下文切换,导致额外的开销;
3、Parallel Scavenge
垃圾收集算法:采用复制算法;
Parallel Scavenge 他跟ParNew一样都支持多线程,主要适用于后台运算且不需要太多交互的任务,且他拥有自动调节策略优势,
Parallel Scavenge重点关心的是吞吐量,高吞吐量可以最高效率利用CPU时间,尽快完成程序运算任务。
注:吞吐量 = 代码运行时间 / (代码运行时间 + 垃圾收集时间) 如果代码运行100min垃圾收集1min,则为99%
二、老年代收集器
1、Serial Old
垃圾收集算法:采用"标记-整理算法",采用单线程模式。
Serial Old的Server模式:jdk5前和Parallel Scavenge搭配使用,jdk5前也只有这个老年代收集器可以和它搭配。jdk5之后作为CMS收集器的后备。
2、Parallel Old
缘来:在jdk6以前,新生代的Parallel Scavenge只能和Serial Old配合使用【根据图,没有这个的话只剩Serial Old,而Parallel Scavenge又不能和CMS配合使用】,而且Serial Old为单线程Server模式下会拖后腿【多核cpu下无法充分利用】,这种结合并不能让应用的吞吐量最大化。
垃圾收集算法:采用"标记-整理算法"【老年代的收集器大都采用此算法】,它采用的多线程模式
Parallel Old 吞吐量:Thoughput, CPU 用于运行用户代码的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间
3、CMS收集器
垃圾收集算法:采用的是"标记-清除"(Mark Sweep)算法
CMS收集器以一种获取最短回收停顿时间为目标的收集器。【重视响应,可以带来好的用户体验,被sun称为并发低停顿收集器】,它支持并发(Concurrent),启用CMS:-XX:+UseConcMarkSweepGC,需要注意的是初始标记和重新标记需要stw(停掉其它运行java线程);
CMS收集器的优缺点归纳为三点:
第一、cms运行线程和应用程序并发执行需要多核cpu;
CMS堆cpu特别敏感,cms运行线程和应用程序并发执行需要多核cpu,如果cpu核数多的话可以发挥它并发执行的优势,但是cms默认配置启动的时候垃圾线程数为 (cpu数量+3)/4,它的性能很容易受cpu核数影响,当cpu的数目少的时候比如说为为2核,如果这个时候cpu运算压力比较大,还要分一半给cms运作,这可能会很大程度的影响到计算机性能。
第二、cms无法处理浮动垃圾,可能导致Concurrent Mode Failure(并发模式故障)而触发full GC;
浮动垃圾:由于cms支持运行的时候用户线程也在运行,程序运行的时候会产生新的垃圾,这里产生的垃圾就是浮动垃圾,cms无法当次处理,得等下次才可以。
第三、存在垃圾碎片的问题;
由于cms是采用"标记-清除“算法,因此就会存在垃圾碎片的问题,为了解决这个问题cms提供了 -XX:+UseCMSCompactAtFullCollection选项,这个选项相当于一个开关【默认开启】,用于CMS顶不住要进行full GC时开启内存碎片合并,内存整理的过程是无法并发的,且开启这个选项会影响性能(比如停顿时间变长)
垃圾收集过程分为4个步骤:
第一、初始标记
只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程
第二、并发标记
进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。
第三、重新标记
为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。
第四、并发清除
清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作, 所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。
三、特殊收集器
1、G1收集器
概述:适用于 Java HotSpot VM 的低暂停、服务器风格的分代式垃圾回收器。G1 GC 使用并发和并行阶段实现其目标暂停时间,并保持良好的吞吐量。当 G1 GC 确定有必要进行垃圾回收时,它会先收集存活数据最少的区域(垃圾优先)
垃圾收集算法:标记-整理、复制算法;
G1收集器他关注与降低延迟,且解决了CMS收集器产生空间碎片等一系列缺陷,它的特别之处是它强化了分区,弱化了分代的概念,是区域化、增量式的收集器,它不属于新生代也不属于老年代收集器。
G1收集器的改进之处:
- 基于标记-整理算法,不产生内存碎片。
- 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收
- G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间, 优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率.
G1收集器优点如下:
第一、G1通过并发(并行)标记阶段查找老年代存活对象,通过并行复制压缩存活对象【这样可以省出连续空间供大对象使用;
第二、G1将一组或多组区域中存活对象以增量并行的方式复制到不同区域进行压缩,从而减少堆碎片,目标是尽可能多回收堆空间【垃圾优先】,且尽可能不超出暂停目标以达到低延迟的目的;
第三、G1提供三种垃圾回收模式 young gc、mixed gc 和 full gc,不像其它的收集器,根据区域而不是分代,新生代老年代的对象它都能回收。
G1收集器的运作大致分为以下几个步骤:
初始标记(Initial Marking):这阶段仅仅只是标记GC Roots能直接关联到的对象并修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确的可用的Region中创建新对象,这阶段需要停顿线程,但是耗时很短。
并发标记(Concurrent Marking):从GC Roots开始对堆的对象进行可达性分析,找出存活的对象,这阶段耗时长,但是可以与用户程序并发执行。
最终标记(Final Marking):为了修正在并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录记录在线程Remembered Set Logs里面。
筛选回收(Live Data Counting and Evacuation):首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这一阶段是可以与用户程序一起并发执行的,但是因为只回收部分Region,时间是用户可控的,而且停顿用户线程将大幅度提高收集效率。
G1收集器在新生代、老年代收集的特点:
G1收集器整体上采用"标记-整理"算法(与标记清除算法略有不同,解决了标记清除算法会产生内存粹片的原因),
局部(两个Region之间)基于"复制回收"算法;
第一、G1的新生代收集特点:
1、一整块堆内存被分为多个Regions.
2、存活对象被拷贝到新的Survivor区或老年代.
3、年轻代内存由一组不连续的heap区组成, 这种方法使得可以动态调整各代区域尺寸.
4、Young GCs会有STW事件, 进行时所有应用程序线程都会被暂停.
5、多线程并发GC.
第二、G1老年代GC特点:
并发标记阶段(index 3)
1.在与应用程序并发执行的过程中会计算活跃度信息.
2.这些活跃度信息标识出那些regions最适合在STW期间回收(which regions will be best to reclaim during an evacuation pause).
3.不像CMS有清理阶段.
再次标记阶段(index 4)
1.使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.
2.空region直接被回收.
拷贝/清理阶段(Copying/Cleanup - Phase)
1.年轻代与老年代同时回收.
2.老年代内存回收会基于他的活跃度信息.