深入学习 GC 算法 - 标记清除算法

news2024/10/10 8:23:45

前言:

📕作者简介:热爱编程的小七,致力于C、Java、Python等多编程语言,热爱编程和长板的运动少年!

📘相关专栏Java基础语法,JavaEE初阶,数据库,数据结构和算法系列等,大家有兴趣的可以看一看。

😇😇😇有兴趣的话关注博主一起学习,一起进步吧!

1、什么是标记清除算法

标记清除算法(Mark-Sweep Algorithm)是一种常见的垃圾回收算法,用于自动管理动态分配的内存空间。其原理如下:

  1. 标记阶段(Mark):从根对象开始,通过引用链追踪,标记所有的活动对象。标记过程中,将活动对象的标记位设置为有效状态,表示这些对象是可达的,不会被回收。

  2. 清除阶段(Sweep):在标记阶段完成后,遍历整个内存空间,将未被标记的对象视为垃圾对象,将其所占用的内存空间释放,以便下次分配给新的对象使用。

标记清除算法通过标记和清除的过程,将不再被使用的内存空间回收,以避免内存泄漏和内存碎片的问题。然而,标记清除算法也存在一些缺点,如清除阶段会产生内存碎片,可能会导致内存分配的效率降低。

为了克服标记清除算法的缺点,还有其他更高级的垃圾回收算法被开发出来,如标记-整理算法和分代收集算法。这些算法通过不同的方式来提高垃圾回收的效率和内存利用率。

标记清除算法 mark_sweep() 函数:

mark_sweep(){
   

     mark_phase()

     sweep_phase()
}

可以看出确实分成了标记阶段和清除阶段。

1688012991506.jpg

2、标记清除算法的优缺点

标记清除算法(Mark-Sweep Algorithm)是一种常见的垃圾回收算法,它的优点和缺点如下:

优点:

  1. 简单易实现:标记清除算法的实现相对简单,只需要进行标记和清除两个阶段的操作。

  2. 可以回收循环引用的对象:标记清除算法可以回收循环引用的对象,即使这些对象之间相互引用,也能正确地标记和清除。

缺点:

  1. 内存碎片问题:标记清除算法在回收垃圾对象后,会产生内存碎片,导致内存空间的利用率降低。这可能会导致后续的内存分配操作变得困难,需要进行内存碎片整理或者使用其他算法来解决。

  2. 垃圾回收过程的停顿:标记清除算法在进行垃圾回收时,需要停止程序的执行,进行标记和清除操作。这会导致一段时间的停顿,可能影响程序的响应性能。

需要注意的是,标记清除算法适用于短生命周期的对象,而对于长生命周期的对象,可能会导致内存碎片问题。为了解决这些问题,可以结合其他垃圾回收算法,如标记-整理算法和分代收集算法,以提高内存利用率和垃圾回收效率。

3、标记阶段

3.1、标记阶段分析

用 mark_phase() 函数来进行标记阶段的处理,函数如下:

mark_phase(){
   
     for(r : $roots)
        mark(*r)
}

mark()函数

mark(obj){
   
     if(obj.mark == FALSE)
         obj.mark = TRUE
         for(child : children(obj))
             mark(*child)
}

标记阶段的具体步骤如下:

  1. 从根节点开始,遍历程序的对象图。根节点可以是全局变量、静态变量、活动线程的栈帧等。
  2. 对于遍历到的每个对象,将其标记为活动对象。可以使用标记位或者其他方式进行标记。
  3. 对于每个已标记的对象,继续遍历其引用的对象,并将其标记为活动对象。这个过程可以递归进行,直到遍历完所有活动对象为止。

标记阶段的关键是通过遍历对象图,将所有活动对象进行标记。标记的方式可以根据具体实现的需求进行选择,常见的方式有使用标记位、使用颜色标记等。

image.png

标记完所有活动对象后,标记阶段就结束了。

3.2、深度优先搜索和广度优先搜索

我们在搜索对象并进行标记时使用的是深度优先搜索(depth-first search)。这是尽可能从深度上搜索树形结构的方法。而且比较一下内存使用量(已存储的对象数量)就可以知道,深度优先搜索比广度优先搜索更能压低内存使用量。因此我们在标记阶段经常用到深度优先搜索。

3.3、深度优先搜索

1688013214945.jpg

深度优先搜索(Depth-First Search,DFS)是一种常用的图遍历算法,它从起始节点开始,沿着一条路径一直深入到无法再继续前进的节点,然后回溯到上一个节点,继续探索其他路径,直到遍历完所有节点。

深度优先搜索的基本思想是尽可能深地搜索图的分支,直到到达叶子节点或无法继续搜索的节点,然后回溯到上一个节点,继续搜索其他分支。具体步骤如下:

  1. 选择一个起始节点,并将其标记为已访问。
  2. 从起始节点开始,选择一个相邻且未被访问过的节点,继续深入搜索。
  3. 如果当前节点没有未访问的相邻节点,则回溯到上一个节点。
  4. 重复步骤2和3,直到遍历完所有节点。

深度优先搜索的特点是优先遍历深度方向,因此可以很快地到达离起始节点较远的节点。然而,由于其采用递归或栈的方式存储节点,可能会导致堆栈溢出的问题。此外,深度优先搜索并不能保证找到最短路径,因为它首先遍历到达目标节点的路径,而不是考虑路径长度。

深度优先搜索在图的遍历、迷宫求解、拓扑排序等问题中有广泛的应用。

3.4、广度优先搜索

1688014866823.jpg

广度优先搜索(Breadth-First Search,BFS)是一种常用的图遍历算法,它从起始节点开始,逐层地向外扩展,先访问离起始节点最近的节点,然后逐渐访问离起始节点越来越远的节点。

广度优先搜索的基本思想是从起始节点开始,依次访问其所有相邻节点,然后再依次访问这些相邻节点的相邻节点,以此类推,直到遍历完所有节点或找到目标节点。

具体步骤如下:

  1. 选择一个起始节点,并将其标记为已访问。
  2. 将起始节点加入队列(FIFO)。
  3. 从队列中取出一个节点,访问该节点,并将其所有未访问过的相邻节点加入队列。
  4. 重复步骤3,直到队列为空。

广度优先搜索的特点是按照距离逐层扩展,先访问离起始节点最近的节点,然后逐渐访问离起始节点越来越远的节点。因此,广度优先搜索可以用于求解最短路径问题。此外,广度优先搜索也可以用于检测图中的环路、拓扑排序等问题。

广度优先搜索通常使用队列来存储待访问的节点,因此不会出现堆栈溢出的问题。但是,由于需要存储所有已访问过的节点,因此空间复杂度较高。

广度优先搜索在图的遍历、寻找最短路径、社交网络分析等领域有广泛的应用。

4、清除阶段

在清除阶段中,collector 会遍历整个堆,回收没有打上标记的对象(即垃圾),使其能再次得到利用。

sweep_phase() 函数

sweep_phase(){
   
     sweeping = $heap_start
     while(sweeping < $heap_end)
         if(sweeping.mark == TRUE)
             sweeping.mark = FALSE
         else
             sweeping.next = $free_list
             $free_list = sweeping
         sweeping += sweeping.size
}

在标记阶段完成后,清除阶段的主要目的是清除未被标记的对象,即那些在程序中没有被引用的对象,并释放它们所占用的内存空间。

清除阶段的具体步骤如下:

  1. 遍历整个堆内存,对于每个对象,检查其是否被标记为活动对象。

  2. 如果对象被标记为活动对象,则保留它,表示它仍然被程序引用,不进行清除。

  3. 如果对象没有被标记为活动对象,则将其标记为未使用的内存空间,并将该内存空间加入到空闲列表中,以便后续的内存分配使用。

  4. 继续遍历堆内存中的所有对象,重复上述步骤,直到清除完所有未被标记的对象。

清除阶段的关键是通过遍历堆内存中的所有对象,将未被标记的对象进行清除。清除的方式可以是将其标记为空闲内存空间,或者直接释放其所占用的内存。

需要注意的是,清除阶段可能会引起内存空间的不连续性,即产生内存碎片。为了解决这个问题,一些优化技术如标记-整理算法、分代收集算法等可以应用于标记清除算法中。

清除阶段完成后,垃圾对象被清除,内存空间被回收,可以供程序进行新的内存分配和使用。

清除阶段结束后:

image.png

5、分配

5.1、什么是分配

在清除阶段已经把垃圾对象连接到空闲链表了。搜索空闲链表并寻找大小合适的分块,这项操作就叫作分配

分配函数 new_obj() 函数:

new_obj(size){
   
     chunk = pickup_chunk(size, $free_list)
     if(chunk != NULL)
         return chunk
     else
         allocation_fail()
}

new_obj()函数接受一个参数size,表示要分配的对象的大小。它调用pickup_chunk()函数来从空闲列表(free_list)中获取一个足够大的内存块。如果成功获取到内存块,则返回该内存块的指针;否则,调用allocation_fail()函数来处理内存分配失败的情况。

5.2、常见的分配算法

First-fit、Best-fit和Worst-fit是三种常见的内存分配算法,用于管理操作系统中的内存分配。它们的具体含义如下:

  1. First-fit(首次适应算法):该算法会在空闲内存块列表中找到第一个能够满足要求的内存块,并将其分配给请求的内存。这种算法的优点是简单快速,但可能会导致较大的内存碎片。

  2. Best-fit(最佳适应算法):该算法会在空闲内存块列表中找到最小的能够满足要求的内存块,并将其分配给请求的内存。这种算法的优点是可以最大限度地减少内存碎片,但可能会导致较长的搜索时间。

  3. Worst-fit(最差适应算法):该算法会在空闲内存块列表中找到最大的能够满足要求的内存块,并将其分配给请求的内存。这种算法的优点是可以减少大型内存块的浪费,但可能会导致较大的内存碎片。

6、合并

根据分配策略的不同可能会产生大量的小分块。但如果它们是连续的,我们就能把所有的小分块连在一起形成一个大分块。这种“连接连续分块”的操作就叫作合并(coalescing),合并是在清除阶段进行的。

合并的函数 sweep_phase() :

sweep_phase(){
   
     sweeping = $heap_start
     while(sweeping < $heap_end)
         if(sweeping.mark == TRUE)
             sweeping.mark = FALSE
         else
             if(sweeping == {
   mathJaxContainer[0]}free_list.size)
                 $free_list.size += sweeping.size
             else
                sweeping.next = $free_list
                $free_list = sweeping
     sweeping += sweeping.size
}

合并函数(merge function)是垃圾回收算法中的一部分,用于合并相邻的空闲内存块,以减少内存碎片。在给定的代码中,合并函数的作用是将相邻的空闲内存块合并为一个更大的空闲内存块。

4、多个空闲链表

标记 - 清除算法中只用到了一个空闲链表,在这个空闲链表中,对大的分块和小的分块进行同样的处理。但是这样一来,每次分配的时候都要遍历一次空闲链表来寻找合适大小的分块,这样非常浪费时间。

因此,我们有一种方法,就是利用分块大小不同的空闲链表,即创建只连接大分块的空闲链表和只连接小分块的空闲链表。这样一来,只要按照 mutator 所申请的分块大小选择空闲链表,就能在短时间内找到符合条件的分块了。

image.png

标记清除算法(Mark-Sweep Algorithm)通常只需要一个空闲链表来管理空闲内存块。这个空闲链表可以是一个简单的单向链表或双向链表,用于存储空闲内存块的起始地址和大小。

在标记阶段,算法会遍历堆中的所有对象,将可达的对象进行标记。在清除阶段,算法会遍历整个堆,将未被标记的对象视为垃圾,进行清除操作。清除操作的结果是产生一些连续的空闲内存块。

在清除阶段结束后,可以将这些连续的空闲内存块组织成多个链表,每个链表对应一个特定的内存块大小范围。这样可以更有效地管理不同大小的空闲内存块,提高内存分配的效率。

例如,可以将空闲内存块按照大小划分为不同的链表,比如一个链表管理大小为4字节的内存块,另一个链表管理大小为8字节的内存块,以此类推。每个链表都有一个头指针指向链表的第一个空闲内存块,空闲内存块之间通过指针进行连接。

这样的设计可以提高内存分配的效率,因为当需要分配一块特定大小的内存时,只需要在对应的链表中查找是否有足够大小的空闲内存块即可。同时,当发生内存释放时,可以将释放的内存块插入到对应大小的链表中,以便后续的内存分配使用。

需要注意的是,多个空闲链表的设计需要维护额外的数据结构和指针,增加了算法的复杂性和内存开销。因此,在实际应用中,根据具体的场景和需求,可以根据性能和内存利用率的权衡来选择是否使用多个空闲链表。

5、BiBOP 法

BiBOP 是 "Big Bag Of Pages" 的缩写。Big Bag Of Pages是一种内存管理策略,常用于操作系统中的虚拟内存管理。它的主要思想是将内存分割成固定大小的页面(Page),并将这些页面组织成一个大的袋子(Big Bag),每个页面可以容纳一个对象或数据结构。

在BiBOP中,内存被组织成一个大的连续空间,每个页面都有固定的大小。当需要分配内存时,会从空闲页面中选择一个足够大的页面来分配。如果页面中剩余的空间不足以满足分配需求,就需要从空闲页面链表中找到一个新的页面。

BiBOP的优点是:

  1. 简单高效:由于页面大小固定且连续存储,分配和释放内存的操作可以高效地进行。
  2. 减少内存碎片:通过将内存划分为固定大小的页面,可以减少内存碎片的发生。

然而,BiBOP也存在一些缺点:

  1. 内部碎片:由于页面大小固定,当分配的对象小于页面大小时,会导致内部碎片的浪费。
  2. 需要较多的内存:为了维护空闲页面链表和页面管理信息,需要额外的内存开销。

image.png

6、位图标记

标记清除算法中的位图标记(Bitmap Marking)是一种常用的标记阶段的实现方式。在标记清除算法中,标记阶段的目标是标记所有活动对象,以便在清除阶段中清除未标记的对象。位图标记通过使用位图数据结构来表示对象的标记状态,以提高标记的效率。

位图是一个由二进制位组成的数据结构,每个位对应一个对象。位图的长度与堆内存的大小相等。初始时,位图中的每个位都被设置为0,表示对象未被标记。

在标记阶段,遍历堆中的所有对象,将已经访问过的对象的对应位设置为1,表示对象已被标记。这样,在遍历完所有对象后,位图中被标记为1的位所对应的对象就是活动对象。

使用位图标记的优点是:

  1. 空间效率高:位图使用的是二进制位,可以有效地节省内存空间。
  2. 访问速度快:位图中的位可以通过位运算来快速访问和修改,提高了标记的效率。

然而,位图标记也有一些缺点:

  1. 需要额外的空间:位图需要额外的空间来存储标记信息,这会增加内存的使用量。
  2. 对象数量受限:位图的长度与堆内存大小相等,因此,位图标记算法对于大型堆或对象数量较多的情况可能会受限。

    7、延迟清除法

延迟清除法(Deferred Sweeping)是标记清除算法的一种变体。在传统的标记清除算法中,标记和清除是依次进行的,即先标记所有的活动对象,然后再清除未标记的对象。而延迟清除法则将清除阶段延迟到下一次的标记阶段之后进行,以减少清除操作对应用程序的影响。

延迟清除法的基本原理是,在标记阶段中,只标记活动对象,而不进行清除操作。将未标记的对象标记为"待清除"状态,以便在下一次的标记阶段之后进行清除。这样可以避免在标记阶段中频繁地进行内存回收操作,减少了对应用程序的影响。

延迟清除法的优点是:

  1. 减少了标记阶段的停顿时间:由于不进行清除操作,标记阶段可以更快地完成,减少了对应用程序的停顿时间。
  2. 提高了垃圾回收的吞吐量:由于清除操作延迟到下一次的标记阶段之后进行,可以在一次清除操作中同时处理多个未标记的对象,提高了垃圾回收的吞吐量。

然而,延迟清除法也有一些缺点:

  1. 可能会增加内存占用:由于延迟了清除操作,被标记为"待清除"状态的对象会占用一定的内存空间,增加了内存的使用量。
  2. 可能会导致内存碎片:延迟清除法并没有解决内存碎片问题,仍然会产生内存碎片,可能会影响到内存的利用效率。

总的来说,延迟清除法是一种可以减少对应用程序影响的标记清除算法变体,适用于对停顿时间敏感且对内存占用要求较低的场景。

 

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

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

相关文章

【蓝凌表单】如何限制明细表字段1与字段2一致时不允许提交

无需开发&#xff0c;表单内置功能快速解决&#xff1b; 有些搞笑&#xff0c;维护蓝凌系统好几年&#xff0c;对系统好多功能也不是很熟悉&#xff0c; 当接到业务需求&#xff0c;不允许某信息跟某信息一致的需求时&#xff0c;第一时间是想到用JS脚本去实现&#xff0c;忽略…

机器学习实战-系列教程3:手撕线性回归2之单特征线性回归(项目实战、原理解读、源码解读)

&#x1f308;&#x1f308;&#x1f308;机器学习 实战系列 总目录 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 手撕线性回归1之线性回归类的实现 手撕线性回归2之单特征线性回归 手撕线性回归3之多特征线性回归 手撕线性回归4之非线性回归# 5…

0-1背包-动态规划

一、01背包 描述&#xff1a;有 N 件物品和一个容量为 V 的背包&#xff0c;每件物品只能使用一次 第 i 件物品的体积是 Ci&#xff0c;价值是 Wi 求解将哪些物品装入背包&#xff0c;能够在不超过背包容量的情况下使总价值最大 求解&#xff1a;动态规划 使用dp[i][j]表示从…

zabbix监控H3C设备

背景 常见的服务和主机已经使用Prometheus进行监控了&#xff0c;但是网络设备还未配置监控。使用基于SNMP对网络设备进行监控。 设备概览 主要类型为H3C的路由器和交换机。H3CS5560交换机 路由器MER5200 er8300 步骤 配置网络设备开启telnet远程&#xff1b; 配置启用sn…

nodejs采集淘宝、天猫网商品详情数据以及解决_m_h5_tk令牌及sign签名验证(2023-09-09)

一、淘宝、天猫sign加密算法 淘宝、天猫对于h5的访问采用了和APP客户端不同的方式&#xff0c;由于在h5的js代码中保存appsercret具有较高的风险&#xff0c;mtop采用了随机分配令牌的方式&#xff0c;为每个访问端分配一个token&#xff0c;保存在用户的cookie中&#xff0c;通…

SAP-MM-销售订单库存转移到普通库存

业务需求&#xff1a; 特殊库存-销售订单库存 有产成品物料1个&#xff0c;现在需要在集团下的两个公司间调拨&#xff0c;需要把特殊库存E调拨到普通库存里&#xff0c;再从H020普通库存调拨到另一个工厂1000. 注意事项&#xff1a;库存地点需要扩充&#xff0c;否则调拨会报…

iOS 17新功能:教你轻松掌握锁定屏幕快捷方式

通过iOS 17&#xff0c;苹果为iPhone用户提供了使用快捷方式锁定手机屏幕的能力。 为什么你需要学习如何使用iOS锁定屏幕快捷方式&#xff1f;按下iPhone上的电源按钮激活这个屏幕肯定是最简单的吗&#xff1f;嗯&#xff0c;这并不总是正确的。如果你在按下物理按钮时遇到困难…

【2023知乎爬虫】批量获取问题的全部回答

一.需求 爬取任意问题下的所有回答&#xff0c;如下图&#xff1a; 1.根据问题&#xff0c;批量获取问题下的所有回答、与对应问题的关系到answer.csv文件&#xff1b; 2.保存当前问题基本信息到quesiton_info.csv文件&#xff1b; 二.展示爬取结果 三.讲解步骤 3.1 新建项…

个人开发者看过来,我搭了一个监控系统免费用

最近在做一个自己的项目&#xff0c;平时就在自己电脑上跑着&#xff0c;有一天回去突然就挂了&#xff0c;查了半天也没搞清楚原因&#xff0c;想看个监控都没有&#xff0c;什么时候挂的&#xff0c;为啥挂了&#xff0c;统统都不知道。平时做公司项目多了&#xff0c;监控用…

C/C++操作加密与不加密的zip文件

为了后续的方便操作zip文件&#xff0c; 将所有的操作封装成了一个动态库了。 /*** \description 从压缩包文件中解压出指定的文件到指定的目录.* \author sunsz* \date 2023/09/09**/ LIBZIP_API int UnpackFile(const char* password, char zipfilename[], char filename_…

rt-thread------任务调度

rt-thread------任务调度 1. 线程初始化 在rt-thread中线程主要包括以下一些内容&#xff0c;线程控制块、线程栈、函数入口。 1.1线程创建函数 RTOS基本都包括两种线程方式&#xff1a;动态创建rt_thread_create()和静态创建rt_thread_init()。 因为有些系统设计时对安全…

硬件学习件Cadence day13 PCB设计中一些设置, 铜皮到钻孔的距离设置, 差分线的设置,板层信息表

1. 设置铺铜中铜皮到钻口&#xff0c;连线的距离。 1. 打开设置界面 2. 设计界面 调整到 铜皮设置界面 2. 高速线的设置 &#xff08;差分对传输线的设置&#xff09; 1. 打开设置界面 2. 来到 差分线设置界面 3. 把界面往右看&#xff0c; 设置差分线的之间距离&#xff0c;…

Python之并发编程介绍

一、并发编程介绍 1.1、串行、并行与并发的区别 串行(serial)&#xff1a;一个CPU上&#xff0c;按顺序完成多个任务并行(parallelism)&#xff1a;指的是任务数小于等于cpu核数&#xff0c;即任务真的是一起执行的并发(concurrency)&#xff1a;一个CPU采用时间片管理方式&am…

TrOCR – 基于 Transformer 的 OCR 入门指南

多年来,光学字符识别 (OCR) 出现了多项创新。它对零售、医疗保健、银行和许多其他行业的影响是巨大的。尽管有着悠久的历史和多种最先进的模型,研究人员仍在不断创新。与深度学习的许多其他领域一样,OCR 也看到了变压器神经网络的重要性和影响。如今,我们拥有像TrOCR(Tran…

franka_ros中的一些子包的使用

franka_visualization包 该软件包包含连接到机器人并发布机器人和夹爪关节状态以在 RViz 中进行可视化的发布者。要运行此包启动&#xff1a; roslaunch franka_visualization franka_visualization.launch robot_ip:<fci-ip> \load_gripper:<true|false> 比如&a…

UI自动化测试工具详解

常用工具 1、QTP&#xff1a;商业化的功能测试工具&#xff0c;收费&#xff0c;可用于web自动化测试 2、Robot Framework&#xff1a;基于Python可扩展的关键字驱动的测试自动化框架 3、Selenium &#xff1a;开源的web自动化测试工具&#xff0c;免费&#xff0c;主要用于功…

SpringCloud-微服务CAP原则

接上文 SpringCloud-Config配置中心 到此部分即微服务的入门。 总的来说&#xff0c;数据存放的节点数越多&#xff0c;分区容忍性就越高&#xff0c;但要复制更新的次数就越多&#xff0c;一致性就越难保证。同时为了保证一致性&#xff0c;更新所有节点数据所需要的时间就…

Python教程33:关于在使用zipfile模块,出现中文乱码的解决办法

zipfile是Python标准库中的一个模块&#xff0c;zipfile里有两个class, 分别是ZipFile和ZipInfo&#xff0c;用来创建和读取zip文件&#xff0c;而ZipInfo是存储的zip文件的每个文件的信息的。ZIP文件是一种常见的存档文件格式&#xff0c;它可以将多个文件和目录压缩为一个文件…

帝国cms后台访问链接提示“非法来源”解决方法

提示“非法来源”的原因 帝国CMS更新升级7.2后,新增了后台安全模式,后台推出了金刚模式来验证链接来源。后台所有链接都需要登录后才能访问,直接强制访问后台页面链接都会提示“非法来源”。不是正常登录后台的用户无法直接访问到内容,保证了后台数据安全。 那么我们在日常…

Table of Laplace Transforms

https://www.math.uh.edu/~etgen/LaplaceT.pdf http://web.mit.edu/2.737/www/handouts/LaplaceTransforms.pdf https://www.integral-table.com/downloads/LaplaceTable.pdf https://www.math.purdue.edu/~caiz/MA527-cai/lectures/Table%20of%20Laplace%20Transforms.pdf