HotSpot的垃圾回收算法实现

来源:互联网 发布:台湾庄园知乎 编辑:程序博客网 时间:2024/05/18 03:28
枚举根节点
可作为GC Roots的节点主要在全局性引用(如常量或者类静态属性)与执行上下文(如栈帧中的本地变量表)中,若直接检查其中的引用,必将耗费很多时间。
可达性分析对执行时间的敏感还体现在GC停顿上,因为该项分析必须在一个能确保一致性的快照中进行(一致性指整个分析期间不能出现引用关系还在不断变化的情况,这样才能确保分析结果的准确性)。这点事导致GC进行时必须停顿所有Java执行线程的一个重要原因。
在HotSpot的实现中,使用一组称为OopMap的数据结构来得知哪些地方存放着对象引用,在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT(即时编译器)编译过程中,也会在特定的位置记录下来栈和寄存器中哪些位置是引用。这样GC在扫描的时候就可以直接得知这些信息了。

安全点
在OopMap的协助下,HotSpot可以快速准确的完成GC Root枚举,但是导致OopMap内容变化(引用关系变化)的指令非常多,如果为每一条指令生成对应的OopMap,会需要很多额外的空间。
实际上,只是在特定的位置记录了这些信息,这些位置称为安全点(程序只有在到达安全点的时候才能暂停进行GC)。安全点的选定基本上是以程序是否具有让程序长时间执行的特征为标准进行选定的——因为每条指令执行的时间都非常的短暂,程序不太可能因为指令流长度提倡这个原因而长时间运行,“长时间运行”最明显的特征就是指令序列复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生safepoint。
对于Safepoint,另一个问题,如何在Gc发生时让所有线程(不包括执行JNI调用的线程)都到最近的安全点再停顿下来。两种方法,抢先式中断和主动式中断。
抢先式中断:GC时,将所有线程全部中断,如果有线程不在安全点上,恢复线程,让其运行到安全点上。
主动式中断:GC需要中断线程的时候,简单的设置一个标志,各个线程主动去轮询这个标志,发现标志位真时就主动中断挂起,轮询标志的地方和安全点是重合的。

安全域

垃圾收集器

新生代收集器(复制算法)
serial收集器
单线程收集器,“单线程”的意义并不仅仅说明它只会使用一个CPU或者一条线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。
应用场景:虚拟机运行在Client模式下的默认的新生代收集器。
原因:简单而高效,没有线程交互的开销,可以获得最高的单线程效率。用户桌面应用场景中,分配给虚拟机管理的内存(新生代)一般只有几十兆到一两百兆。客户端的停顿时间控制在几十毫秒最多一百毫秒内,只要不是频繁发生,这点停顿是可以接受的。

ParNew收集器
Serial收集器的多线程版本,使用多条线程进行垃圾收集,其余行为与Serial一样。
应用场景:Server模式下首选的新生代收集器,一个与性能无关但是重要的原因是,除了Serial,只有它能与CMS收集器配合工作。

Parallel Scavenge收集器(吞吐量优先)
新生代收集器,使用复制算法来进行收集,并行的多线程收集器,与ParNew收集器类似,但是它的特点是比较关注吞吐量。
吞吐量=运行用户代码时候/(运行用户代码事件+垃圾收集时间)
特点:Parallel Scavenge收集器的目的是达到一个可控制的吞吐量。吞吐量即CPU用户运行用户代码的时间与CPU总消耗时间的比值。
应用场景:适合在后台运算而不需要交互太多的任务。
Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,-XX:MaxGCPauseMillis参数控制最大垃圾收集停顿时间,-XX:GCTimeRatio参数直接设置吞吐量大小。
参数-XX:+UseAdaptiveSizePolicy是一个开关参数,当这个参数打开后,就不需要手工指定新生代的大小、Eden和Survivor的比例、晋升老年代对象的大小等细节参数,虚拟机会根据当前系统的运行情况收集性能监控信息,动态地调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略。
GC自适应的调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。

老年代收集器
Serial Old收集器(标记--整理算法)
Serial收集器的老年代版本,同样是一个单线程收集器
应用场景:Client模式下的虚拟机。如果使用在Server模式下,那它主要还有两大用途途:1.JDk1.5及之前的版本中与Parallel Scavenge收集器搭配使用,2.作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

Parallel old收集器(标记--整理算法)
Parallel Scavenge收集器的老年代版本,多线程
该收集器在JDK1.6中才开始提供

CMS收集器(标记--清除算法)
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的JAVA应用集中在互联网网站或者B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望停顿越短越好,以给用户带来较好的用户体验。CMS收集器就非常符合这类的应用需求。
CMS(concurrent Mark Sweep)是基于“标记--清除”算法实现的,它的运作过程分为四个步骤:
1.初始标记
仅仅标记一下GC Roots能直接关联到的对象,速度很快
2.并发标记
进行GC RootsTracing的过程
3.重新标记
为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。此阶段的停顿时间比初始标记阶段长比并发标记短。
4.并发清除
初始标记和重新标记仍然需要“Stop The World”,但是并发标记和并发清除过程可以和用户线程一起工作,总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

CMS缺点:
1.CMS收集器对CPU资源非常敏感
2.CMS收集器无法处理浮动垃圾,可能出现cconcurrent Mode Failure而导致另一次Full GC的产生
3.产生大量的空间碎片

G1收集器
JDK1.7中
G1是一款面向服务端应用的垃圾收集器,它的使命是未来可以替换掉JDK1.5中发布的CMS收集器。
G1的特点:
1.并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-the-world停顿的事件,G1收集器可以通过并发的方式让Java程序继续执行
2.分代收集:分代概念,虽然G1不需要其他收集器的配合就能独立管理整个Java堆,但是可以采用不同的方式去处理不同的对象。
3.空间整合:G1整体看来是基于标记整理的算法,从局部看来确实基于复制的算法实现。
4.可预测的停顿
G1之前的收集器进行收集的范围都是整个新生代或者老年代,而使用G1收集器时Java堆的内存布局就发送了变化,他将整个java堆划分为多个大小相等的独立区域(Region)。新生代和老年代是一部分Region的集合。
G1收集器的步骤:
1.初始标记
2.并发标记
3.最终标记
4.筛选回收


原创粉丝点击