垃圾回收算法优缺点对比

img_830c2a85da95ec10021fcb48d4370609.png
image.png

GC之前

说明:该文中的GC算法讲解不仅仅局限于某种具体开发语言。

mutator

mutator 是 Edsger Dijkstra 、 琢磨出来的词,有“改变某物”的意思。说到要改变什么,那就是 GC 对象间的引用关系。不过光这么说可能大家还是不能理解,其实用一句话概括的话,它的实体就是“应用程序”。这样说就容易理解了吧。GC 就是在这个 mutator 内部精神饱满地 工作着。

mutator 实际进行的操作有以下 2 种。

  • 生成对象
  • 更新指针

mutator 在进行这些操作时,会同时为应用程序的用户进行一些处理(数值计算、浏览网页、 编辑文章等)。随着这些处理的逐步推进,对象间的引用关系也会“改变”。伴随这些变化会 产生垃圾,而负责回收这些垃圾的机制就是 GC。

活动对象 / 非活动对象

我们将分配到内存空间中的对象中那些能通过 mutator 引用的对象称为“活动对象”。反 过来,把分配到堆中那些不能通过程序引用的对象称为“非活动对象”。

根(root)这个词的意思是“根基”“根底”。在 GC 的世界里,根是指向对象的指针的“起点” 部分。

这些都是能通过 mutator 直接引用的空间。

评价标准

评价 GC 算法的性能时,我们采用以下 4 个标准。

  • 吞吐量
  • 最大暂停时间
  • 堆使用效率
  • 访问的局部性

其他信息(Java语言为例):JVM解读-GC(垃圾回收)


1 标记-清除法

优点

①实现简单

说到 GC 标记 - 清除算法的优点,那当然要数算法简单,实现容易了。
另外,如果算法实现简单,那么它与其他算法的组合也就相应地简单。

②与保守式 GC 算法兼容

后面介绍的保守式 GC 算法中,对象是不能被移动的。因此保守式 GC 算法跟把 对象从现在的场所复制到其他场所的 GC 复制算法与标记 - 压缩算法不兼容。

而 GC 标记 - 清除算法因为不会移动对象,所以非常适合搭配保守式 GC 算法。事实上,在很多采用保守式 GC 算法的处理程序中也用到了 GC 标记 - 清除算法。

缺点

①碎片化

在 GC 标记 - 清除算法的使用过程中会逐渐产生被细化的分块,不久后就会导致无数的 小分块散布在堆的各处。我们称这种状况为碎片化(fragmentation)。众所周知,Windows 的 文件系统也会产生这种现象。

img_6f0119442ac486e3da4376397a0b615b.png
image.png

②分配速度

GC 标记 - 清除算法中分块不是连续的,因此每次分配都必须遍历空闲链表,找到足够 大的分块。最糟的情况就是每次进行分配都得把空闲链表遍历到最后。

另一方面,因为在 GC 复制算法和 GC 标记 - 压缩算法中,分块是作为一个连续的内存 空间存在的,所以没必要遍历空闲链表,分配就能非常高速地进行,而且还能在堆允许范围 内分配很大的对象。

③与写时复制技术不兼容

写时复制技术(copy-on-write)是在 Linux 等众多 UNIX 操作系统的虚拟存储中用到的高速化方法。在 Linux 中复制进程,也就是使用 fork() 函数时,大部分内存空间都不会被复制。只是复制进程,就复制了所有内存空间的话也太说不过去了吧。因此,写时复 制技术只是装作已经复制了内存空间,实际上是将内存空间共享了。

在各个进程中访问数据时,能够访问共享内存就没什么问题了。
然而,当我们对共享内存空间进行写入时,不能直接重写共享内存。因为从其他程序访 问时,会发生数据不一致的情况。在重写时,要复制自己私有空间的数据,对这个私有空间 进行重写。复制后只访问这个私有空间,不访问共享内存。像这样,因为这门技术是“在写 入时进行复制”的,所以才被称为写时复制技术。

这样的话,GC 标记 - 清除算法就会存在一个问题 — 与写时复制技术不兼容。即使没 重写对象,GC 也会设置所有活动对象的标志位,这样就会频繁发生本不应该发生的复制, 压迫到内存空间。

2 引用计数的算法

优点

①可即刻回收垃圾

在引用计数法中,每个对象始终都知道自己的被引用数(就是计数器的值)。当被引用数 的值为 0 时,对象马上就会把自己作为空闲空间连接到空闲链表。也就是说,各个对象在变成垃圾的同时就会立刻被回收。

另一方面,在其他的 GC 算法中,即使对象变成了垃圾,程序也无法立刻判别。只有当 分块用尽后 GC 开始执行时,才能知道哪个对象是垃圾,哪个对象不是垃圾。也就是说,直 到 GC 执行之前,都会有一部分内存空间被垃圾占用。

②最大暂停时间短

在引用计数法中,只有当通过 mutator 更新指针时程序才会执行垃圾回收。也就是说, 每次通过执行 mutator 生成垃圾时这部分垃圾都会被回收,因而大幅度地削减了 mutator 的最 大暂停时间。

③没有必要沿指针查找

引用计数法和 GC 标记 - 清除算法不一样,没必要由根沿指针查找。减少沿指针查找的次数。

缺点

①计数器值的增减处理繁重

在引用计数法中,每当指针更新时,计数器的值都会随之更新,因此值的增减处理必然会变得繁重。

②计数器需要占用很多位

用于引用计数的计数器最大必须能数完堆中所有对象的引用数。

③实现烦琐复杂

引用计数的算法本身很简单,但事实上实现起来却不容易。如果漏掉了某处,内存管理就无法正确 进行,就会产生 BUG。

④循环引用无法回收

两个对象互相引用,所以各对象的计数器的值都是 1。但是这些对象 组并没有被其他任何对象引用。因此想一并回收这两个对象都不行,只要它们的计数器值都 是 1,就无法回收。

3 GC 复制算法

优点

①优秀的吞吐量

GC 标记 - 清除算法消耗的吞吐量是搜索活动对象(标记阶段)所花费的时间和搜索整体 堆(清除阶段)所花费的时间之和。

另一方面,因为 GC 复制算法只搜索并复制活动对象,所以跟一般的 GC 标记 - 清除算 法相比,它能在较短时间内完成 GC。也就是说,其吞吐量优秀。

尤其是堆越大,差距越明显。GC 标记 - 清除算法在清除阶段所花费的时间会不断增加, 但 GC 复制算法就不会产生这种消耗。毕竟它消耗的时间是与活动对象的数量成比例的。

②可实现高速分配

GC 复制算法不使用空闲链表。这是因为分块是一个连续的内存空间。比起 GC 标记 - 清除算法和引用计数法等使用空闲链表的分配,GC 复制算法明显快得多。

③不会发生碎片化

基于算法性质,活动对象被集中安排在 From 空间的开头对吧。像这 样把对象重新集中,放在堆的一端的行为就叫作压缩。在 GC 复制算法中,每次运行 GC 时 都会执行压缩。

因此 GC 复制算法有个非常优秀的特点,就是不会发生碎片化。也就是说,可以安排分 块允许范围内大小的对象。

④与缓存兼容

在 GC 复制算法中有引用关系的对象会被安排在堆里离彼此较近的位置。这种情况有一个优点,那就是 mutator 执行速度极快。这也是借助压缩来完成的,通过压缩来把有引用关系的对 象安排在堆中较近的位置。

缺点

①堆使用效率低下

GC 复制算法把堆二等分,通常只能利用其中的一半来安排对象。也就是说,只有一半 堆能被使用。相比其他能使用整个堆的 GC 算法而言,可以说这是 GC 复制算法的一个重大的缺陷。

通过搭配使用 GC 复制算法和 GC 标记 - 清除算法可以改善这个缺点。

②不兼容保守式 GC 算法

GC 标记 - 清除算法有着跟保守式 GC 算法相兼容的优点。因 为 GC 标记 - 清除算法不用移动对象。

另一方面,GC 复制算法必须移动对象重写指针,所以有着跟保守式 GC 算法不相容的 性质。

③递归调用函数

在这里介绍的算法中,复制某个对象时要递归复制它的子对象。因此在每次进行复制的 时候都要调用函数,由此带来的额外负担不容忽视。大家都知道比起这种递归算法,迭代算 法更能高速地执行

此外,因为在每次递归调用时都会消耗栈,所以还有栈溢出的可能。

4 GC标记-压缩算法

优点

①可有效利用堆

在 GC 标记 - 压缩算法中会执行压缩,和其他算法相比而言,堆利用效率高。
而且 GC 标记 - 压缩算法不会出现 GC 复制算法那样只能利用半个堆的情况。

另一方面,尽管 GC 标记 - 清除算法也能利用整个堆,但因为没有压缩的过程,所以会 产生碎片化,不能充分有效地利用堆。

缺点

①压缩花费计算成本

在 GC 标记 - 清除算法中,清除阶段也要搜索整个堆,不过搜索 1 次就够了。但 GC 标记 - 压缩算法要搜索 3 次,这样就要花费约 3 倍的时间,这是一个相当巨大的缺陷,特别是堆越 大,所消耗的成本也就越大。

4 保守式 GC

什么是保守式 GC

简单来说,保守式 GC(Conservative GC)指的是“不能识别指针和非指针的 GC”。

优点

①语言处理程序不依赖于 GC

保守式 GC 的优点在于容易编写语言处理程序。处理程序基本上不用在意 GC 就可以编 写代码。语言处理程序的实现者即使没有意识到 GC 的存在,程序也会自己回收垃圾。因此 语言处理程序的实现要比准确式 GC 简单。

缺点

①识别指针和非指针需要付出成本

②错误识别指针会压迫堆

当存在貌似指针的非指针时,保守式 GC 会把被引用的对象错误识别为活动对象。如果 这个对象存在大量的子对象,那么它们一律都会被看成活动对象。因为程序把已经死了的非 活动对象看成了活动对象,所以垃圾对象会严重压迫堆。

③能够使用的 GC 算法有限

5 分代垃圾回收

优点

①吞吐量得到改善

通过使用分代垃圾回收,可以改善 GC 所花费的时间(吞吐量)。正如 Ungar 所说的那样:“据实验表明,分代垃圾回收花费的时间是 GC 复制算法的 1/4。”可见分代垃圾 回收的导入非常明显地改善了吞吐量。

另一方面,因为老年代 GC 很费时间,所以我们没法缩短 mutator 的最大暂停时间。关 于使用分代垃圾回收来缩减 mutator 最大暂停时间的方法

缺点

①在部分程序中会起到反作用

对对象会活得很久的程序执行分代垃圾回收,就会产生以下两个问题。

  • 新生代GC所花费的时间增多
  • 老年代GC频繁运行

考虑到这两点,恐怕我们没法利用到分代垃圾回收的优点,或者就算利用到了,效果也 甚微。

6 增量式垃圾回收

优点

①缩短最大暂停时间

增量式垃圾回收不是一口气运行 GC,而是和 mutator 交替运行的,因此不会长时间妨碍 到 mutator 的运行。

增量式垃圾回收适合那些比起提高吞吐量,更重视缩短最大暂停时间的应用程序。

②降低了吞吐量

想要优先提高吞吐量,最大暂停时间就会增加;想要优先缩短最大暂停时间, 吞吐量就会恶化。这两者是一个权衡关系。至于要优先哪一方,则要根据应用程序而定。

参考文献:《垃圾回收的算法与实现》


欢迎关注 高广超的简书博客 与 收藏文章 !
欢迎关注 头条号:互联网技术栈 !

个人介绍:

高广超:多年一线互联网研发与架构设计经验,擅长设计与落地高可用、高性能、可扩展的互联网架构。

本文首发在 高广超的简书博客 转载请注明!

img_31e2e3075b097cabfd9b3643cd9abaa5.png
简书博客
img_3b1610566b09db3358ca5dcb3e015a52.png
头条号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390817.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

标准C程序设计七---77

Linux应用 编程深入 语言编程标准C程序设计七---经典C11程序设计 以下内容为阅读: 《标准C程序设计》(第7版) 作者:E. Balagurusamy(印), 李周芳译 清华大学出版社…

leetcode 1190. 反转每对括号间的子串

题目 给出一个字符串 s(仅含有小写英文字母和括号)。 请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。 注意,您的结果中 不应 包含任何括号。 示例 1: 输入&#xff1a…

yolo人脸检测数据集_自定义数据集上的Yolo-V5对象检测

yolo人脸检测数据集计算机视觉 (Computer Vision) Step by step instructions to train Yolo-v5 & do Inference(from ultralytics) to count the blood cells and localize them.循序渐进的说明来训练Yolo-v5和进行推理(来自Ultralytics )以对血细胞进行计数并将其定位。 …

oauth2-server-php-docs 授权类型

授权码 概观 在Authorization Code交付式时使用的客户端想要请求访问受保护资源代表其他用户(即第三方)。这是最常与OAuth关联的授予类型。 详细了解授权码 用例 代表第三方来电履行 创建一个实例OAuth2\GrantType\AuthorizationCode并将其添加到您的服务…

flask框架视图和路由_角度视图,路由和NgModule的解释

flask框架视图和路由Angular vs AngularJS (Angular vs AngularJS) AngularJS (versions 1.x) is a JavaScript-based open source framework. It is cross platform and is used to develop Single Page Web Application (SPWA). AngularJS(版本1.x)是一个基于JavaScript的开源…

NGUI EventDelagate事件委托

using System.Collections; using System.Collections.Generic; using UnityEngine;public class BUttonClick : MonoBehaviour {public UIButton button_01;void Start(){if (button_01 null){Debug.Log("button组件丢失了");}else{//首先将脚本中的ClicktheButton…

leetcode 461. 汉明距离(位运算)

两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。 给出两个整数 x 和 y&#xff0c;计算它们之间的汉明距离。 注意&#xff1a; 0 ≤ x, y < 231. 示例:输入: x 1, y 4输出: 2解释: 1 (0 0 0 1) 4 (0 1 0 0)↑ ↑上面的箭头指出了对应二进…

图深度学习-第2部分

有关深层学习的FAU讲义 (FAU LECTURE NOTES ON DEEP LEARNING) These are the lecture notes for FAU’s YouTube Lecture “Deep Learning”. This is a full transcript of the lecture video & matching slides. We hope, you enjoy this as much as the videos. Of cou…

Linux下 安装Redis并配置服务

一、简介 1、 Redis为单进程单线程模式&#xff0c;采用队列模式将并发访问变成串行访问。 2、 Redis不仅仅支持简单的k/v类型的数据&#xff0c;同时还提供list&#xff0c;set&#xff0c;zset&#xff0c;hash等数据结构的存储。 3、 Redis支持数据的备份&#xff0c;即mas…

大omega记号_什么是大欧米茄符号?

大omega记号Similar to big O notation, big Omega(Ω) function is used in computer science to describe the performance or complexity of an algorithm.与大O表示法相似&#xff0c;大Omega(Ω)函数在计算机科学中用于描述算法的性能或复杂性。 If a running time is Ω…

leetcode 477. 汉明距离总和(位运算)

theme: healer-readable 题目 两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。 计算一个数组中&#xff0c;任意两个数之间汉明距离的总和。 示例: 输入: 4, 14, 2 输出: 6 解释: 在二进制表示中&#xff0c;4表示为0100&#xff0c;14表示为1110&…

什么是跨域及跨域请求资源的方法?

1、由于浏览器同源策略&#xff0c;凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。 2、跨域请求资源的方法&#xff1a; (1)、porxy代理(反向服务器代理) 首先将用户发送的请求发送给中间的服务器&#xff0c;然后通过中间服务器再发送给后台服…

量子信息与量子计算_量子计算为23美分。

量子信息与量子计算On Aug 13, 2020, AWS announced the General Availability of Amazon Braket. Braket is their fully managed quantum computing service. It is available on an on-demand basis, much like SageMaker. That means the everyday developer and data scie…

全面理解Java内存模型

Java内存模型即Java Memory Model&#xff0c;简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型&#xff0c;所以JMM是隶属于JVM的。 如果我们要想深入了解Java并发编程&#xff0c;就要先理解好Java内存模型。Java内存模型定义了多…

React Native指南

React本机 (React Native) React Native is a cross-platform framework for building mobile applications that can run outside of the browser — most commonly iOS and Android applicationsReact Native是一个跨平台框架&#xff0c;用于构建可在浏览器外部运行的移动…

leetcode 1074. 元素和为目标值的子矩阵数量(map+前缀和)

给出矩阵 matrix 和目标值 target&#xff0c;返回元素总和等于目标值的非空子矩阵的数量。 子矩阵 x1, y1, x2, y2 是满足 x1 < x < x2 且 y1 < y < y2 的所有单元 matrix[x][y] 的集合。 如果 (x1, y1, x2, y2) 和 (x1’, y1’, x2’, y2’) 两个子矩阵中部分坐…

失物招领php_新奥尔良圣徒队是否增加了失物招领?

失物招领phpOver the past couple of years, the New Orleans Saints’ offense has been criticized for its lack of wide receiver options. Luckily for Saints’ fans like me, this area has been addressed by the signing of Emmanuel Sanders back in March — or has…

教你分分钟使用Retrofit+Rxjava实现网络请求

撸代码之前&#xff0c;先简单了解一下为什么Retrofit这么受大家青睐吧。 Retrofit是Square公司出品的基于OkHttp封装的一套RESTful&#xff08;目前流行的一套api设计的风格&#xff09;网络请求框架。它内部使用了大量的设计模式&#xff0c;以达到高度解耦的目的&#xff1b…

线程与进程区别

一.定义&#xff1a; 进程&#xff08;process&#xff09;是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。 进程中所包含的一个或多个执行单元称为线程&#xff08;thread&#xff09;。进程还拥有一个私有的虚拟地址空间&#xff0c;该空间…

基本SQL命令-您应该知道的数据库查询和语句列表

SQL stands for Structured Query Language. SQL commands are the instructions used to communicate with a database to perform tasks, functions, and queries with data.SQL代表结构化查询语言。 SQL命令是用于与数据库通信以执行任务&#xff0c;功能和数据查询的指令。…