垃圾回收相关算法之垃圾回收阶段算法

2、垃圾回收阶段算法

当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存。目前在JVM中比较常见的三种收集算法是:
(1)标记-清除算法(Mark-Sweep)
(2)复制算法(Copying)
(3)标记-压缩算法(Mark-Compact)

2.1标记-清除算法

执行过程
当堆中的有效内存空间(available memory)被耗尽时 ,就会停止整个程序(也被称为 stop the world),然后进行两项工作,第一项是标记,第二项则是清除。
标记: Collector从引用根节点开始遍历,**标记所有被引用的对象。**一般是在对象的Header中记录为可达对象。(注意:标记的是被引用的对象,也就是可达对象,并非标记的是即将被清除垃圾的对象)。
清除: Collector对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收。
在这里插入图片描述
标记-清除算法的优点:

  1. 非常基础和常见的垃圾收集算法容易理解。
    标记-清除算法的缺点:
  2. 标记清除算法的效率不算高
  3. 在进行GC的时候,需要停止整个应用程序,用户体验较差
  4. 这种方式清理出来的空闲内存是不连续的,产生内碎片,需要维护一个空闲列表(空闲列表-记录垃圾对象地址)。
    注意:何为清除?
    这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的 地址列表里。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果够,就存放(也就是覆盖原有的地址)。

2.2复制算法

为了解决标记-清除算法在垃圾收集效率方面的缺陷,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。在垃圾回收时将正在使用的呃逆存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。
在这里插入图片描述
复制算法的优缺点
优点:
5. 没有标记和清除过程,实现简单,运行高效
6. 复制过去以后保证空间的连续性,不会出现“碎片”问题。
缺点:
7. 此算法的缺点也是很明显的,就是需要两倍的内存空间。
8. 对于G1这种分拆称为大量的region的GC,复制而不是移动,意味着GC需要维护region之间对象的引用关系,不管是内存占用或者时间开销也不小。
复制算法的应用场景
如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大,效率较高。
老年代大量的对象存活,那么复制的对象将会有很多,效率会很低。
在新生代,对常规应用的垃圾回收,一次通常可以回收70%-99%的内存空间。
回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
在这里插入图片描述

2.3标记-压缩算法

背景
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活对象较多,复制分成本也将很高。因此,基于老年代垃圾回收的特性,需要使用其他的算法。
标记清除算法的确可以应用在老年代中,但是该算法不仅执行效率低下,而且在执行玩内存回收后还会产生内存碎片,所以JVM设计者需要在此基础上进行改进。
标记压缩算法执行过程
第一阶段和标记清除算法一样,从根节点开始标记所有被引用对象
第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。之后,清理边界外所有的空间。
在这里插入图片描述
标记-压缩算法与标记-清除算法的比较
标记-压缩算法的最终效果等同于标记-清除算法执行完成后,再进行一次内存碎片整理,因此,也可以把它称为标记–清除–压缩(Mark-Sweep-Compact)算法。
二者的本质差异在于标记-清除算法是一种非移动式的回收算法(空闲列表记录 位置),标记-压缩是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策。
可以看到,标记的存活对象将会被整理,按照内存地址一次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。
标记-压缩算法的优缺点
优点:
消除了标记-清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存 时,JVM 只需要持有一个内存的起始地址即可。 消除了复制算法当中,内存减半的高额代价。
缺点:
从效率上来说,标记-整理算法要低于复制算法。 移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址 移动过程中,需要全程暂停用户应用程序。即:STW

2.4垃圾回收算法小结

效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存。 而为了尽量兼顾上面提到的三个指标,标记-整理算法相对来说更平滑一些,但是效率上不尽如人意,它比复制算法多了一个标记的阶段,比标记-清除多了一 个整理内存的阶段。
在这里插入图片描述

2.5分代收集

首先,我们提出为什么要使用分代收集?
前面所有这些算法中,并没有一种算法可以完全替代其他算法,它们都具有自己 独特的优势和特点。分代收集应运而生。
分代收集,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不 同生命周期的对象可以采取不同的收集方式,以便提高回收效率。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点使用不同的回收算法, 以提高垃圾回收的效率。
在 Java 程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相 关:
比如 Http 请求中的 Session 对象、线程、Socket 连接,这类对象跟业务直接 挂钩,因此生命周期比较长。 但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期 会比较短,比如:
String 对象,由于其不变类的特性,系统会产生大量的这些对 象,有些对象甚至只用一次即可回收。
目前几乎所有的GC都是采用分代收集算法执行垃圾回收的。
在 HotSpot 中,基于分代的概念,GC 所使用的内存回收算法必须结合年轻代 和老年代各自的特点。
年轻代(Young Gen)
年轻代特点:区域相对老年代较小,对象生命周期短、存活率低,回收频繁。
这种情况复制算法的回收整理,速度是最快的。复制算法的效率只和当前存活对 象大小有关,因此很适用于年轻代的回收。而复制算法内存利用率不高的问题, 通过 hotspot 中的两个 survivor 的设计得到缓解。
老年代(Tenured Gen)
老年代特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。
这种情况存在大量存活率高的对象,复制算法明显变得不合适。一般是由标记- 清除或者是标记-清除与标记-整理的混合实现。

  1. Mark阶段的开销与存活对象的数量成正比。
  2. Sweep阶段的开销与所管理区域的大小成正相关。
  3. Compact阶段的开销与存活对象的数据成正比。
    分代的思想被现有的虚拟机广泛使用。几乎所有的垃圾回收器都区分新生代和老年代。

小结:

垃圾回收阶段算法

标记-清除算法

​ 分为两个阶段:

标记: 标记出从根可达的对象,标记的是被引用的对象.

清除:
此清除并非直接将垃圾对象清除掉.而是将垃圾对象的地址维护到一个空闲列表中,之后如果有新的对象产生,判断空闲列表中的对象空间能否存放得下新的对象,如果能放得下,那么就覆盖垃圾对象.

优点: 简单,容易理解

缺点: 效率低, 会产生STW(在回收时,停止整个应用程序), 会产生内存碎片.

2.复制算法

将内存分为大小相等的两块,每次只使用其中的一块儿区域即可.

当回收时,将不是垃圾的对象,复制到另一块内存中,排放整齐.

然后将原来的内存块清空.

减少内存碎片.

优点: 运行高效,减少内存碎片

缺点: 用到两倍的内存空间 ,对于G1垃圾回收器,将每个区域又可以拆分成更多的小区域,需要维护各区之间的关系.

在新生代中的幸存者0和幸存者1这两个区域使用复制算法.

3.标记压缩算法

背景:

​ 复制算法需要移动对象位置,移动的数量如果多的情况下,效率低. 对于年轻代来讲还是不错的.

对于老年代,大量的对象是存活的. 如果需要移动就比较麻烦效率低.

实现:

将存活对象标记出来,重新在本内存空间中排放位置.

清除其他空间的垃圾对象.


标记-清除 和 标记-压缩对比

标记清除是不移动对象, 不会把垃圾对象清除掉(维护在一个空闲列表中)

标记-压缩是要移动对象的. 要清除掉垃圾对象.

优点:
不会像标记-清除算法那样会产生内存碎片

不会像复制算法那样需要两块内存空间

缺点:

效率相对低, 对象位置移动后需要重新设置对象地址, 也会有STW


分代/分区收集

由于对象的生命周期长短不同,将不同的对象存储在不同的区域.

针对不同的区域进行分区收集,提高收集效率.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值