JVM系列(四)垃圾收集算法和垃圾收集器

通过上篇文章,我们了解到了在进行GC垃圾回收时,如何判断对象是否是垃圾对象是否可以被回收,那么被标记为垃圾的对象是如何被回收的,被回收后jvm运行时数据区的内存分配又会变成什么样子,本篇文章将逐一进行解答。


常用垃圾收集算法

1. 标记-清除算法(Mark-Sweep)

这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:
在这里插入图片描述
缺点:
1.效率问题,标记和清除这两个过程的效率都不高
2.例如上图所示,将黑色的都回收之后,造成内存中的存活对象和可用空间分布比较零散,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

2. 复制算法(Copying)

为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题

复制算法的提出是为了克服句柄的开销和解决内存碎片的问题。它开始时把堆分成 一个对象 面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,基于copying算法的垃圾 收集就从根集合(GC Roots)中扫描活动对象,并将每个 活动对象复制到空闲面,这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。

优点: 这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。

缺点: 复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低

3. 标记-整理算法(Mark-compact)

为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动然后清理掉端边界以外的内存。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。
在这里插入图片描述
4. 分代收集算法(Generational Collection)

(1) 根据对象存活周期的不同将内存划分为几块。
(2) 一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
(3) 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
(4) 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。


常见的垃圾收集器

对于垃圾收集器,本文将着重讲解CMS和G1收集器

1.Serial收集器:(-XX:+UseSerialGC)

(1) 是一个单线程的收集器,“Stop The World”
(2) 对于运行在Client模式下的虚拟机来说是一个很好的选择
(3) 简单而高效
(4) 新生代使用复制算法
在这里插入图片描述
2. Serial Old收集器:(-XX:+UseSerialOldGC)

(1) Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。
(2) 主要意义也是在于给Client模式下的虚拟机使用。
(3) 如果在Server模式下,那么它主要还有两大用途:
一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用;另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用

3. ParNew收集器:(-XX:+UseParNewGC)

1、Serial收集器的多线程版本
2、单CPU不如Serial
3、Server模式下新生代首选,目前只有它能与CMS收集器配合工作
在这里插入图片描述
4. Parallel Scavenge收集器

(1) 吞吐量优先”收集器
(2) 新生代收集器,复制算法,并行的多线程收集器
(3) 目标是达到一个可控制的吞吐量(Throughput)。
(4) 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
(5) 两个参数用于精确控制吞吐量:
-XX:MaxGCPauseMillis是控制最大垃圾收集停顿时间(控制用户线程停顿时间)
-XX:GCTimeRatio直接设置吞吐量大小
-XX:+UseAdaptiveSizePolicy:动态设置新生代大小、Eden与Survivor区的比例、晋升老年代对象年龄
(6) 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
(7) 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。

5. Parallel Old收集器

(1) Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
(2) 在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。
在这里插入图片描述
6. CMS(Concurrent Mark Sweep)收集器:(-XX:+UseConcMarkSweepGC):

CMS(Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用,它是Hotspot虚拟机第一次真正意义上的并发收集器,它第一次实现了让垃圾收集线程和用户线程同时工作.

1、以获取最短回收停顿时间为目标的收集器。
2、非常符合互联网站或者B/S系统的服务端上,重视服务的响应速度,希望系统停顿时间最短的应用
3、基于“标记—清除”算法实现的(会有内存碎片存在)
4、CMS收集器的内存回收过程是与用户线程一起并发执行的
5、它的运作过程分为4个步骤,包括:
初始标记: “Stop The World”,只是标记一下GC Roots能直接关联到的对象,速度很快。
并发标记: 同时开启GC和用户线程,用一个闭包结构去记录可达对象,但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程会不断地更新引用域,所以GC线程无法保证可达性分析的实时性。并发标记阶段就是进行GC RootsTracing(可达性分析)的过程。
重新标记: Stop The World”,是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这段时间的停顿时间一般比初始标记的时间稍长,但远比并发标记的时间短。
并发清除(CMS concurrent sweep): 开启用户线程,同时GC线程开始对未标记的区域做清扫。
在这里插入图片描述

主要优点: 并发收集、低停顿。但是他有以下几个明显的缺点

对CPU资源敏感(会和服务抢资源);

它使用的回收算法“标记-清除”会导致收集结束时会有大量的空间碎片产生,当然可以通过参数
-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后在做整理;

执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收系统一边运行,也许没回收完就再次触发Full gc,也就是“concurrent mode failure”,此时会进入stop the word,用serial old垃圾收集器来回收

CMS收集器中有几个参数需要额外注意:
-XX:+UseCMSCompactAtFullCollection :Full GC之后做压缩处理,减少空间碎片
-XX:+CMSInitiatingOccupancyFraction:当老年代使用达到该比例时会触发Full GC,(默认值是-1,单位是百分比)。

7. G1(Garbage-First)收集器:(-XX:+ UseG1GC)

主要针对配备多核处理器及大容量内存的机器。
G1将java堆划分为多个大小相等的独立区域(Region),JVM最多可以有2048个Region
一般一个Region大小等于堆大小除以2048,比如堆大小为4096M,则Region大小为2M,当然也可以用参数**-XX:G1HeapRegionSize** 手动指定Region大小。
默认年轻代对堆内存的占比为5%,如果堆大小为4096M,那么年轻代占据200M左右的内存,对应大概是100个Region,可以通过-XX:G1NewSizePrecent设置新生代初始占比,在系统运行时,JVM会不停地给年轻代增加更多的Region,但是最多新生代的占比不会超过60%,可以通过**-XXG1MaxNewSizePrecent** 调整。年轻代中的Eden和Survivor对应的region也和之前一样,默认8:1。
一个Region可能之前是年轻代,如果Region进行了垃圾回收,之后可能又会变成老年代,也就是说Region的区域功能可能会动态变化
G1有专门分配大对象的Region叫Humongous区,而不是让大对象直接进入老年代的Region中。在G1中,大对象的判定规则就是一个大对象超过了一个Region大小的50%,就会被放入Humongous中,而且一个大对象如果太大,可能会横跨多个Region来存放。

它的运行过程分为以下几个步骤:

初始标记: 标记一下GC Roots能直接关联到的对象,需要停顿线程,但耗时很短

并发标记: 是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行(同CMS并发标记)

最终标记: 修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录(同CMS重新标记)

筛选回收:(较CMS特殊之处) 筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间(可以用JVM参数-XX:MaxGCPauseMillis 默认200毫秒指定)来制定回收计划,比如老年代现在有1000个Region都满了,但是因为根据预期停顿时间,本次垃圾回收可能只能停顿2000毫秒,那么通过之前回收成本计算得知,可能回收其中800个Region刚好需要2000毫秒,那么就只回收800个Region。尽量把GC导致的停顿控制在我们预期设定的值范围内。这个阶段其实也可以做到与用户线程并发执行,但是因为只回收其中一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高回收效率。年轻代、老年代回收算法主要采用复制算法。将一个Region中存活的对象复制到另一个Region中,这种不会像CMS那样回收完因为有很多内存碎片还需要整理一次,G1采用复制算法几乎不会有很多碎片
在这里插入图片描述
优点:
并行与并发: 充分利用多CPU、多核环境下的硬件优势
分代收集: 不需要其他收集器配合就能独立管理整个GC堆
空间整合: “标记—整理”算法实现的收集器,局部上基于“复制”算法不会产生内存空间碎片
可预测的停顿: 能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒

G1收集器中的三种GC

YoungGC

YoungGC并不是说现有的Eden区域放满了就会马上触发,G1在计算下现在Eden区回收大概需要多久时间,如果回收时间远远小于参数-XX:MaxGCPauseMillis 设定的值,那么增加年轻代的Region,继续给新对象存放,不会马上做YoungGC,直到下一次Eden区放满,G1计算回收时间接近-XX:MaxGcPauseMillis设定的值,那么就会触发YoungGC。

MixedGC

不是FullGC,老年代的堆占有率达到参数(-XX:+CMSInitiatingOccupancyFraction)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定Old区垃圾收集的优先级)以及大对象区,正常情况G1的垃圾收集是先做Mixed GC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空的region能够承载拷贝对象,就会触发一次Full GC。

Full GC

停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出一批Region来供下一次MixedGC使用,这个过程非常耗时。

在这里插入图片描述
两个收集器间有连线,表明它们可以搭配使用:
Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;
若使用不可组合的GC会报(-XX:+UseParNewGC -XX:+UseParallelOldGC):
Conflicting collector combinations in option list


图片参考自:《深入理解Java虚拟机:JVM高级特性与最佳实践》

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值