【C 数据结构-动态内存管理】3. 伙伴系统管理动态内存

2024-05-08 07:52

本文主要是介绍【C 数据结构-动态内存管理】3. 伙伴系统管理动态内存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 【 1. 伙伴系统的结构设计 】
  • 【 2. 分配算法 】
  • 【 3. 回收算法 】

  • 伙伴系统 本身是一种动态管理内存的方法,和边界标识法的区别是:使用伙伴系统管理的存储空间,无论是空闲块还是占用块,大小都是 2 的 n 次幂(n 为正整数)。
    例如,系统中整个存储空间为 2 m 2^m 2m 个字。那么在进行若干次分配与回收后,可利用空间表中只可能包含空间大小为: 2 0 2^0 20 2 1 2^1 21 2 2 2^2 22、…、 2 m 2^m 2m 的空闲块。

【 1. 伙伴系统的结构设计 】

  • 伙伴系统中可利用空间表中的结点构成如下图所示:
    header 域表示为头部结点,由 4 部分构成:
    • llinkrlink 为结点类型的指针域,分别用于指向直接前驱和直接后继结点。
    • tag :用于标记内存块的状态,是占用块(用 1 表示)还是空闲块(用 0 表示)
    • kval :记录该存储块的容量。由于系统中各存储块都是 2 的 m 幂次方,所以 kval 记录 m 的值。
      在这里插入图片描述
  • 伙伴系统节点的 C 实现:
typedef struct WORD_b{struct WORD_b *llink;//指向直接前驱int tag;//记录该块是占用块还是空闲块int kval;//记录该存储块容量大小为2的多少次幂struct WORD_b *rlink;//指向直接后继OtherType other;//记录结点的其它信息
}WORD_b,head;
  • 在伙伴系统中,由于系统会不断地接受用户的内存申请的请求,所以会产生很多大小不同但是容量都是为 2 m 2^m 2m 的内存块,所以为了在分配的时候查找方便,系统将大小相同的内存块建立一个链表。对于初始容量为 2 m 2^m 2m 的一整块存储空间来说,形成的链表就有可能有 m+1 个,为了更好的对这些链表进行管理,系统将这 m+1 个链表的表头存储在数组中,就类似于邻接表的结构,如下图所示:
    在这里插入图片描述
  • 可利用空间表的 C 实现:
#define m 16//设定m的初始值
typedef struct HeadNode {int nodesize;//记录该链表中存储的空闲块的大小WORD_b * first;//相当于链表中的next指针的作用
}FreeList[m+1];//一维数组

【 2. 分配算法 】

  • 伙伴系统的分配算法很简单。假设用户向系统申请大小为 n 的存储空间,若 2 k − 1 < n ≤ 2 k 2^{k-1} < n \leq 2^k 2k1<n2k,此时就需要查看可利用空间表中大小为 2 k 2^k 2k 的链表中有没有可利用的空间结点:
    • 如果该链表不为 NULL,可以直接采用头插法从头部取出一个结点,提供给用户使用;
    • 如果大小为 2k 的链表为 NULL,就需要依次查看比 2 k 2^k 2k 大的链表,找到后从链表中删除,截取相应大小的空间给用户使用,剩余的空间,根据大小插入到相应的链表中。
  • 例如,用户向系统申请一块大小为 7 个字的空间,而系统总的内存为 24 个字,则此时按照伙伴系统的分配算法得出: 2 2 2^2 22 < 7 < 2 3 2^3 23,所以此时应查看可利用空间表中大小为 2 3 2^3 23 的链表中是否有空闲结点:
    • 如果有,则从该链表中摘除一个结点,直接分配给用户使用;
    • 如果没有,则需依次查看比 2 3 2^3 23 大的各个链表中是否有空闲结点。假设,在大小 2 4 2^4 24 的链表中有空闲块,则摘除该空闲块,分配给用户 2 3 2^3 23 个字的空间,剩余 2 3 2^3 23 个字,该剩余的空闲块添加到大小为 2 3 2^3 23 的链表中,如下图所示:
      在这里插入图片描述
  • 使用伙伴系统进行存储空间的管理过程中,在用户申请空间时,由于大小不同的空闲块处于不同的链表中,所以 分配完成的速度会更快,算法相对简单

【 3. 回收算法 】

  • 无论使用什么内存管理机制,在内存回收的问题上都会面临一个共同的问题:如何把回收的内存进行有效地整合,伙伴系统也不例外。
  • 当用户申请的内存块不再使用时,系统需要将这部分存储块回收,回收时需要判断是否可以和其它的空闲块进行合并。在寻找合并对象时,伙伴系统和边界标识法不同,在伙伴系统中每一个存储块都有各自的 伙伴当用户释放存储块时只需要判断该内存块的伙伴是否为空闲块,如果是则将其合并,然后合并的新的空闲块还需要同其伙伴进行判断整合,反之直接将存储块根据大小插入到可利用空间表中即可
  • 判断一个存储块的伙伴的位置时,采用的方法为:如果该存储块的起始地址为 p,大小为 2 k 2^k 2k,则其伙伴所在的起始地址为:
    { p + 2 k ,若 p 取余 2 k + 1 = 0 p − 2 k ,若 p 取余 2 k + 1 = 2 k \begin{cases} p+2^k,若p取余2^{k+1}=0\\\\ p-2^k,若p取余2^{k+1}=2^k\end{cases} p+2k,若p取余2k+1=0p2k,若p取余2k+1=2k
  • 例如,当大小为 2 8 2^8 28 ,起始地址为 512 的伙伴块的起始地址的计算方式为:
    由于 512 取余 2 9 2^9 29=0,所以,512+ 2 8 2^8 28=768,即如果该存储块回收时,只需要查看起始地址为 768 768 768 的存储块的状态,如果是空闲块则两者合并,反之直接将回收的释放块链接到大小为 2 8 2^8 28 的链表中。
  • 回收存储空间时,对于 空闲块的合并,不是取决于该空闲块的相邻位置的块的状态;而是 完全取决于其伙伴块。所以即使其相邻位置的存储块时空闲块,但是由于两者不是伙伴的关系,所以也不会合并。这也就是该系统的缺点之一:由于 在合并时只考虑伙伴,所以容易产生存储的碎片

这篇关于【C 数据结构-动态内存管理】3. 伙伴系统管理动态内存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/969750

相关文章

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

在Node.js中使用.env文件管理环境变量的全过程

《在Node.js中使用.env文件管理环境变量的全过程》Node.js应用程序通常依赖于环境变量来管理敏感信息或配置设置,.env文件已经成为一种流行的本地管理这些变量的方法,本文将探讨.env文件... 目录引言为什么使php用 .env 文件 ?如何在 Node.js 中使用 .env 文件最佳实践引

python库pydantic数据验证和设置管理库的用途

《python库pydantic数据验证和设置管理库的用途》pydantic是一个用于数据验证和设置管理的Python库,它主要利用Python类型注解来定义数据模型的结构和验证规则,本文给大家介绍p... 目录主要特点和用途:Field数值验证参数总结pydantic 是一个让你能够 confidentl

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

redis数据结构之String详解

《redis数据结构之String详解》Redis以String为基础类型,因C字符串效率低、非二进制安全等问题,采用SDS动态字符串实现高效存储,通过RedisObject封装,支持多种编码方式(如... 目录一、为什么Redis选String作为基础类型?二、SDS底层数据结构三、RedisObject