垃圾收集器与内存分配策略——垃圾收集算法与HotSpot虚拟机算法实现

垃圾收集算法的具体实现涉及大量的程序细节,这里只描述其算法的基本思想和发展过程

一、常见的垃圾收集算法对比如下

收集算法具体实现优点不足
标记-清除算法

1、首先标记出所有需要回收的对象

2、标记完成之后,统一回收所有被标记的对象

 

效率低:标记和清除两个过程效率都不高

空间问题:标记清除之后又大量的碎片,空间碎片太对会导致以后需要分配大对象时无法找到连续的内存空间而触发垃圾收集动作

复制算法

1、将可用内存划分成两块,每次只使用其中的一块。

2、当这一块内存用完了,就将还存活的对象复制到另外一块内存上面,然后把使用过的内存空间一次性清空

相比标记-清除算法的实现来说,实现简单,运行高效

这种算法的实现是将可用内存划分,可用空间减小。

 

IBM公司的专门研究表明,89%的对象都是朝生夕死,所以并不需要按照1:1来划分内存空间。HotSpot虚拟机中,按照8:1:1的比例,划分Eden空间和两个Survivor空间

 

Survivor空间不足时,需要依赖其他内存(老年代)进行分配担保
标记-整理算法

1、标记过程跟标记-清除算法一样

2、让可存活的对象向着一端移动,然后清理掉端边界外的内存

适用于老年代

 
分代收集算法

分代收集算法没有什么新的思想,只是按照对象存活的周期不同,将内存划分为几块,这样就可以跟进每一块年代的特点,选用最合适的收集算法

  

二、HotSpot虚拟机算法实现

前面介绍了判断对象存活的判定算法和垃圾收集算法,而在HotSpot虚拟机上实现这些算法时,必须对算法的执行效率有严格的考量,才能保证虚拟机高效执行。 

1、枚举根节点

从可达性分析对中从GC Roots节点找到引用链这个操作为例,可作为GC Roots的节点主要在全局引用和执行上下文中。现在很多应用仅方法区就能达到数百兆,如果要逐个检查这里面的引用,那么必然会消耗很多的时间。

另外,可达性分析对执行时间的铭感还体现在GC 停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行。这里的一致性是指整个分析期间整个执行系统看起来就像被冻结在某个时间点上一样。不可以出现分析过程中对象引用关系还在变化的情况。这点是导致GC执行时必须停顿所有的Java执行线程(Sun公司将这件事称为Stop The World)的原因,即使是号称不会发生停顿的CMS收集器中,枚举根节点时也必须要进行停顿。

当执行系统停顿下来之后,虚拟机并不需要一个不漏地检查完所有的执行上线文和全局变量的引用未知,虚拟机应该有办法得到哪些地方存放着对象引用。在HotSpot虚拟机中,是使用一组称为OopMap的数据结构来达到这个目的的。

2、安全点

在OopMap的协助下,HotSpot虚拟机快速完成GC Roots枚举,但是一个很现实的问题随之而来,可能导致引用关系发生变化,或者说能使OopMap内容变化的指令非常多。如果为每一个指令都生成对应的OopMap的话,那GC的成本将变得很高。实际上,HotSpot虚拟机也的确没有为每条指令都生成OopMap,只有在特定的位置记录了这些信息。这些位置就称为安全点(Safepoint),也就是说程序并非在所有的笛梵都能停下来进行GC,只有在到达安全点时,才能进行暂停。

SafePoint的选定,既不能让GC等待时间太长,也不能过于频繁。所以安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的——因为每条指令执行的时间都非常短暂,程序不可能因为指令流长度太长这个原因而过长时间运行。长时间执行的最显著特征就是指令序列复用。例如方法调用,循环跳转,异常跳转等地方,所以具备这些功能的指令才会产生SafePoint。

3、安全区域

使用SafePoint 似乎已经完美解决了程序进入GC的问题,但是,如果再不太长的时间内,就遇到可进入的GC的SafePoint,程序又不执行(例如程序没有分配到CUP时间片,或者线程处于Sleep状态),这时候线程无法响应JVM的中断请求。对于这种情况,就需要安全区域(Safe Region)来解决

安全区域时指在一段代码片段中,引用关系不会发生变化,在这个区域中的任意地方开始GC都是可安全的,我们也可以把Safe Region 看做是扩展了的SafePoint。

三、垃圾收集器

垃圾收集算法是方法论,垃圾收集器是具体实现。Java虚拟机规范中对垃圾收集器如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机厂商提供的垃圾收集器都会有很大的差别。

在基于JDK1.7 Update 14 之后的HotSpot虚拟机中,包含的所有收集器如下图所示:

如上的7中垃圾收集器,用实现连接起来的代表可以搭配使用。

需要明确的一点是:

如上列出来的所有收集器,没有说那个最好,因为直到现在为止,还没有最好的收集器出现

1、Serial收集器

Serial收集器是最基本,也是发展最悠久的收集器,是一个单线程的收集器。他的单线程的意义并不仅仅说明它只会使用一个CPU或者一条收集线程完成垃圾回收,更重要的是在它进行垃圾回收时,必须暂停其他所有的工作线程。直到它完成垃圾回收

2、ParNew 收集器

ParNew 收集器是Serial收集器的多线程版本,除了使用多线程完成垃圾回收外,其余行为包括Serial收集器可用的所有控制参数,收集算法,Stop The World,对象分配规则,回收策略,等都与Serial收集器完全一样。

ParNew收集器是许多运行在Server模式下虚拟机的首选新生代收集器,其中一个重要的原因就是,除Serial收集器外,目前只有它能与CMS收集器配合使用。ParNew收集器是在开启 -XX:+UseConcMarkSweepGC 选项后的默认新生代收集器,也可以通过制定 -XX:UseParNewGC 选项来强制使用它。

ParNew收集器在单CPU的环境下,绝对不会有比Serial收集器更好的效果。在多CPU的环境下,可以使用 -XX:+ParallelGCThreads 参数来限制垃圾收集器的线程数

3、Parallel Scavenge 收集器

Parallel Scavenge 收集器是新生代收集器,也是使用复制算法,看上去与ParNew 收集器一样,但是与其他收集器的关注点不同,CMS等收集器的关注点是尽可能缩短垃圾收集时间和用户线程的停顿时间。而Parallel Scavenge 收集器的目标则是达到一个可控的吞吐量

吞吐量:运行用户代码的时间/(运行用户代码的时间 + 垃圾收集时间),如果虚拟机总共运行100分钟,垃圾收集花掉1分钟,那吞吐量就
是99%

停顿时间越短,就越适合与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数),以及直接设置吞吐量大小的-XX:GCTimeRatio参数。

-XX:MaxGCPauseMillis 是一个大于0的毫秒数,但是不能认为将该值设置的小一点就可以使系统的垃圾收集速度变快,缩短GC停顿时间是以牺牲吞吐量和新生代空间来换取的(例如:将新生代的空间从500M调整到300M,这样看似收集起来会快一点,但是收集的频率会增加,原来10秒收集一次,每次停顿100毫秒,现在可能5秒就需要收集一次,每次停顿70毫秒,停顿时间的确在降低,但是吞吐量也下降了)

-XX:GCTimeRatio 参数的值应该是一个大于0,且小于100的整数,也就是垃圾收集时间占总时间的比率。

Parallel Scavenge 收集器有一个开关参数:-XX:+UseAdaptiveSizePolicy,当这个参数添加之后,就不需要手动指定新生代大小,Eden与Survivor的比例,晋升老年代对象的大小了等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数也提供最合适的停顿时间或者最大的吞吐量。这种调节方式成为GC自适应的调节策略。

4、Serial Old 收集器

Serial Old 收集器是一个老年代收集器,因为使用但线程收集,所以效率一般,使用标记-整理算法

5、Parallel Old 收集器

Parallel Old 收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。Parallel Old 收集器出现之后,吞吐量优先的收集器(Parallel Scavenge)终于有了比较名副其实的应用组合。在注重吞吐量以及CPU资源铭感的应用中,都可以优先考虑Parallel Scavenge 和 Parallel Old收集器。

6、CMS 收集器

CMS 收集器是一款以获得最短停顿时间为目标的收集器,CMS 收集器是基于标记-清除算法实现的。它的运作过程较前面几个收集器稍微复杂一些。

HotSpot虚拟机所使用的垃圾收集器对比

垃圾收集器特点适用年代实现算法
Serial收集器

1、单线程收集器,进行垃圾回收时必须暂停所有的工作线程

2、是虚拟机运行在Client模式下的默认新生代收集器 

新生代复制算法
ParNew收集器

1、是Serial收集器的多线程版本,使用多线程进行垃圾回收外

2、是运行在Server模式下虚拟机的首选新生代收集器

3、ParNew收集器是在开启 -XX:+UseConcMarkSweepGC 选项后的默认新生代收集器,也可以通过制定 -XX:UseParNewGC 选项来强制使用它。

新生代复制算法
Parallel Scavenge收集器

1、使用多线程

2、Parallel Scavenge 收集器与其他收集器的关注点不同,CMS等收集器的关注点是尽可能缩短垃圾收集时间和用户线程的停顿时间。而Parallel Scavenge 收集器的目标则是达到一个可控的吞吐量,所以Parallel Scavenge 收集器是一个吞吐量优先的收集器

新生代复制算法
Serial Old 收集器

1、单线程老年代收集器,效率一般。

老年代标记-整理算法
Parallel Old 收集器

1、Parallel Scavenge 收集器的老年代版本

老年代标记-整理算法
CMS 收集器

1、CMS(Concurrent Mark Sweep)以获取最短的回收停顿时间为目标

2、收集过程包括:

      初始标记:标记GC Roots能直接关联到的对象,速度很快。

      并发标记:进行GC Roots Tracing的过程。

      重新标记

      并发清除

3、整个过程中耗时最长的是并发标记和并发清除两个过程,都是和工作线程并发执行

缺点:

1、对CPU资源比较敏感

2、CMS 收集器无法处理浮动垃圾

3、因为CMS收集器使用标记-清除算法,所以会产生大量的空间碎片。好在CMS收集器提供了一个开关参数,用于在CMS收集器顶不住要进行FULL GC 的时候合并整理过程。因为合并整理是无法并发执行的,所以不得不延长停顿时间

老年代标记-清除算法
G1 收集器

G1收集器最前沿的成果之一,它有如下特点

1、并行,并发

2、分代收集

3、空间整合

4、可预测的停顿

新生代,老年代 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值