【C 数据结构-动态内存管理】2. 边界标识法管理动态内存

文章目录

  • 【 1. 边界标识法的结构设计 】
  • 【 2. 分配算法 】
  • 【 3. 回收算法 】
    • 3.1 空闲块两侧是占用块
    • 3.2 空闲块左侧是空闲块
    • 3.3 空闲块右侧是空闲块
    • 3.3 空闲块两侧是空闲块

  • 边界标识法 可以解决系统中内存碎片过多而无法使用的问题。

【 1. 边界标识法的结构设计 】

  • 使用边界标识法的可利用空间表本身是 双向循环链表,每个内存块结点都有指向前驱和后继结点的指针域。在使用边界标识法管理内存时,可利用空间表中的结点的构成如下图所示:每个结点中包含 3 个区域,head 域、foot 域 和 space 域:
    • space 域 表示为该内存块的大小,它的大小通过 head 域中的 size 值表示。
    • head 域 中包含有 4 部分:
      • llink 和 rlink 分别表示指向当前内存块结点的直接前驱和直接后继;
      • tag 值用于标记当前内存块的状态,是占用块(用 1 表示)还是空闲块(用 0 表示);
      • size 用于记录该内存块的存储大小。
    • foot 域 中包含有 2 部分:
      • uplink 是指针域,用于指向内存块本身,通过 uplink 就可以获取该内存块所在内存的首地址;
      • tag 同 head 域中的 tag 相同,都是记录内存块状态的。
    • PS:head 域和 foot 域在本节中都假设只占用当前存储块的 1 个存储单位的空间,对于该结点整个存储空间来说,可以忽略不计。也就是说,在可利用空间表中,知道下一个结点的首地址,该值减 1 就可以找到当前结点的 foot 域。

在这里插入图片描述

  • 边界标识法管理的内存块结点的代码表示为:
typedef struct WORD{
    union{
        struct WORD *llink;//指向直接前驱
        struct WORD *uplink;//指向结点本身
    };
    int tag;//标记域,0表示为空闲块;1表示为占用块
    int size;//记录内存块的存储大小
    struct WORD *rlink;//指向直接后继
    OtherType other;//内存块可能包含的其它的部分
}WORD,head,foot,*Space;
  • 通过以上介绍的结点结构构建的可利用空间表中,由于是双向循环链表结构,任何一个结点都可以作为该链表的头结点(用 pav 表示头结点),当头结点为 NULL 时,即可利用空间表为空,无法继续分配空间。

【 2. 分配算法 】

  • 问题描述
    当用户申请空间时,系统可以采用 首部拟合法、最佳拟合法和最差拟合法 这 3 种分配方法中的任何一种。但在不断分配的过程中,会产生一些容量极小以至无法利用的空闲块,这些不断生成的小内存块就会减慢遍历分配的速度。
  • 针对上述问题的解决措施是:
    1. 选定一个常量 e,每次向用户分配空间后,判断内存块剩余部分的容量,如果剩余部分的容量比 e 小,则将整个内存块全部分配给用户。
    2. 采用首部拟合法进行分配时,如果每次都从 pav 指向的结点开始遍历,在若干次后,会出现存储量小的结点密集地分布在 pav 结点附近的情况,严重影响遍历的时间。解决办法就是:在每次分配空间后,让 pav 指针指向该分配空间结点的后继结点,然后从新的 pav 指向的结点开始下一次的分配。
  • 分配算法的 C实现(采用首部拟合法):
Space AllocBoundTag(Space *pav,int n){
    Space p,f;
    int e=10;//设定常量 e 的值
    //如果在遍历过程,当前空闲块的存储容量比用户申请空间 n 值小,在该空闲块有右孩子的情况下直接跳过
    for (p=(*pav); p&&p->size<n&&p->rlink!=(*pav); p=p->rlink);
    //跳出循环,首先排除p为空和p指向的空闲块容量小于 n 的情况
    if (!p ||p->size<n) {
        return NULL;
    }else{
        //指针f指向p空闲块的foot域
        f=FootLoc(p);
        //调整pav指针的位置,为下次分配做准备
        (*pav)=p->rlink;
        //如果该空闲块的存储大小比 n 大,比 n+e 小,负责第一种情况,将 p 指向的空闲块全部分配给用户
        if (p->size-n <= e) {
            if ((*pav)==p) {
                pav=NULL;
            }
            else{
                //全部分配用户,即从可利用空间表中删除 p 空闲块
                (*pav)->llink=p->llink;
                p->llink->rlink=(*pav);
            }
            //同时调整head域和foot域中的tag值
            p->tag=f->tag=1;
        }
        //否则,从p空闲块中拿出 大小为 n 的连续空间分配给用户,同时更新p剩余存储块中的信息。
        else{
            //更改分配块foot域的信息
            f->tag=1;
            p->size-=n;
            //f指针指向剩余空闲块 p 的底部
            f=FootLoc(p);
            f->tag=0;   f->uplink=p;
            p=f+1;//p指向的是分配给用户的块的head域,也就是该块的首地址
            p->tag=1;   p->size=n;
        }
        return p;
    }
}

【 3. 回收算法 】

  • 问题描述
    在用户活动完成,系统需要立即回收被用户占用的存储空间,以备新的用户使用。回收算法中需要解决的问题是:在若干次分配操作后,可利用空间块中会产生很多存储空间很小以致无法使用的空闲块。但是经过回收用户释放的空间后,可利用空间表中 可能含有地址相邻的空闲块,回收算法需要 将这些地址相邻的空闲块合并为大的空闲块供新的用户使用
  • 合并空闲块有 3 种情况:
    • 该空闲块的左边有相邻的空闲块可以进行合并;
    • 该空闲块的右边用相邻的空闲块可以进行合并;
    • 该空闲块的左右两侧都有相邻的空闲块可以进行合并;
  • 判断当前空闲块左右两侧是否为空闲块的方法是:
    对于当前空闲块 p ,p-1 就是相邻的低地址处的空闲块的 foot 域,如果 foot 域中的 tag 值为 0 ,表明其为空闲块; p+p->size 表示的是高地址处的块的 head 域,如果 head 域中的 tag 值为 0,表明其为空闲块。

3.1 空闲块两侧是占用块

  • 如果当前空闲块的左右两侧都不是空闲块,而是占用块,此种情况下只需要 将新的空闲块按照相应的规则(头部拟合法随意插入,其它两种方法在对应位置插入)插入到可利用空间表 中即可。
  • C 实现:
//设定p指针指向的为用户释放的空闲块
p->tag=0;
//f指针指向p空闲块的foot域
Space f=FootLoc(p);
f->uplink=p;
f->tag=0;
//如果pav指针不存在,证明可利用空间表为空,此时设置p为头指针,并重新建立双向循环链表
if (!pav) {
    pav=p->llink=p->rlink=p;
}else{
    //否则,在p空闲块插入到pav指向的空闲块的左侧
    Space q=pav->llink;
    p->rlink=pav;
    p->llink=q;
    q->rlink=pav->llink=p;
    pav=p;
}

3.2 空闲块左侧是空闲块

  • 如果该空闲块的左侧相邻的块为空闲块,右侧为占用块,处理的方法是:只需要 更改左侧空闲块中的 size 的大小,并重新设置左侧空闲块的 foot 域 即可(如下图所示)。
    在这里插入图片描述
  • C 实现:
//常量 n 表示当前空闲块的存储大小
int n=p->size;
Space s=(p-1)->uplink;//p-1 为当前块的左侧块的foot域,foot域中的uplink指向的就是左侧块的首地址,s指针代表的是当前块的左侧存储块
s->size+=n;//设置左侧存储块的存储容量
Space f=p+n-1;//f指针指向的是空闲块 p 的foot域
f->uplink=s;//这是foot域的uplink指针重新指向合并后的存储空间的首地址
f->tag=0;//设置foot域的tag标记为空闲块

3.3 空闲块右侧是空闲块

  • 如果用户释放的内存块的相邻左侧为占用块,右侧为空闲块,处理的方法为:将用户释放掉的存储块替换掉右侧的空闲块,同时更改存储块的 size 和右侧空闲块的 uplink 指针的指向(如下图所示)。
    在这里插入图片描述
  • C 实现:
Space t=p+p->size;//t指针指向右侧空闲块的首地址
p->tag=0;//初始化head域的tag值为0
//找到t右侧空闲块的前驱结点和后继结点,用当前释放的空闲块替换右侧空闲块
Space q=t->llink;
p->llink=q; q->rlink=p;
Space q1=t->rlink;
p->rlink=q1; q1->llink=p;
//更新释放块的size的值
p->size+=t->size;
//更改合并后的foot域的uplink指针的指向
Space f=FootLoc(t);
f->uplink=p;

3.3 空闲块两侧是空闲块

  • 如果当前用户释放掉的空闲块,物理位置上相邻的左右两侧的内存块全部为空闲块,需要将 3 个空闲块合并为一个更大的块,操作的过程为:更新左侧空闲块的 size 的值,同时在可利用空间表中摘除右侧空闲块,最后更新合并后的大的空闲块的 foot 域
    此情况和只有左侧有空闲块的情况雷同,唯一的不同点是多了一步摘除右侧相邻空闲块结点的操作。
  • C 实现:
int n=p->size;
Space s=(p-1)->uplink;//找到释放内存块物理位置相邻的低地址的空闲块
Space t=p+p->size;//找到物理位置相邻的高地址处的空闲块
s->size+=n+t->size;//更新左侧空闲块的size的值
//从可利用空间表中摘除右侧空闲块
Space q=t->llink;
Space q1=t->rlink;
q->rlink=q1;
q1->llink=q;
//更新合并后的空闲块的uplink指针的指向
Space f=FootLoc(t);
f->uplink=s;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

绘唐ai工具怎么获取

这款产品的最大亮点在于其高度精准的语音克隆能力&#xff0c;利用先进的模型&#xff0c;能够捕捉到用户独特的音调、音高和调制方式&#xff0c;使用户能够以前所未有的方式复制和利用自己的声音。仅需10秒钟的录制时间&#xff0c;即可实现声音的克隆&#xff0c;相当便捷。…

Spring Cloud 整合Sentinel

1、引入依赖 版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 父pom <spring.cloud.version>Hoxton.SR12</spring.cloud.version> <spring.cloud.alibaba.version>2.2.10-RC1</spring.cloud.alibaba.version>Sentinel应用直接引用starter <…

基于FPGA的数字密码锁电路Verilog代码Quartus仿真

名称&#xff1a;基于FPGA的数字密码锁电路Verilog代码Quartus仿真(文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;Verilog 代码功能&#xff1a; 数字密码锁电路的设计 1.设计任务:设计并制作数字密码锁电路 2.设计要求 1.用EDA实训仪的I/设备和PLD志…

纯血鸿蒙APP实战开发——折叠屏扫描二维码方案

折叠屏扫描二维码方案 介绍 本示例介绍使用自定义界面扫码能力在折叠屏设备中实现折叠态切换适配。自定义界面扫码使用系统能力customScan&#xff0c;其提供相机流的初始化、启动扫码、识别、停止扫码、释放相机流资源等能力。折叠屏折叠状态通过监听display的foldStatusCha…

Hive3.0新特性:Materialized Views 物化视图

Materialized Views 物化视图 在 Apache Hive 3.0 中引入了物化视图&#xff08;Materialized Views&#xff09;的支持&#xff0c;它们是预先计算并缓存了查询结果的数据结构&#xff0c;以提高查询性能和降低延迟。物化视图通过将查询的结果存储在物理表中来实现&#xff0…

深入理解指针1

目录 如对您有帮助&#xff0c;还望三连支持&#xff0c;谢谢&#xff01;&#xff01;&#xff01; 1.内存和地址 计算机中常⻅的单位&#xff08;补充&#xff09;&#xff1a; 如何理解编址 2.指针变量和地址 2.1取地址操作符&#xff08;&&#xff09; 2.2指针变…

数据结构学不会?数据结构可视化网站来了

目录 前言 图码网站 算法可视化 算法编辑器 数据结构全书 数据结构课程 总结 前言 数据结构与算法在计算机的学习中应该是许多小白最头疼的东西&#xff0c;明明听的时候那么容易&#xff0c;为什么转换成代码就那么抽象呢&#xff1f; 有没有一个网站可以数据结构与算…

【RabbitMQ 三】Java客户端开发

本文引用的代码源自《RabbitMQ实战指南》 关键的类和接口主要有Channel、Connection、ConnectionFactory、Consumer等&#xff0c;它们主要的作用如下&#xff1a; Channel&#xff1a;实现AMQP协议层的操作Connection&#xff1a;开启信道&#xff08;Channel&#xff09;、注…

k8s集群统一设置时间

1 安装时间同步需要软件 yum install -y ntpdate2 设置时间 2.1 手动设置时间 date -s "20190712 18:30:50" hwclock --systohc2.2 在线更新时间 ntpdate 0.asia.pool.ntp.org # 强制把系统时间写入CMOS clock -w3 强制把系统时间写入CMOS hwclock作用与clock相…

【复杂网络】如何用简易通俗的方式快速理解什么是“相对重要节点挖掘”?

什么是相对重要节点&#xff1f; 一、相对重要节点的定义二、如何区分相对重要节点与重要节点&#xff1f;1. 相对重要性与节点相似性2. 识别相对重要节点的两个阶段第一阶段&#xff1a;个体重要性值的计算第二阶段&#xff1a;累积重要性值的计算 三、经典的相对重要节点挖掘…

08 - 条件判断语句

---- 整理自狄泰软件唐佐林老师课程 文章目录 1. 条件判断语句2. 语法说明3. 经验4. 代码 1. 条件判断语句 makefile 中支持条件判断语句 可以根据条件的值来决定 make 的执行可以比较两个不同变量或者变量和常量的值 注&#xff1a;条件判断语句只能用于控制 make 实际执行的…

探索大模型能力--prompt工程

1 prompt工程是什么 1.1 什么是Prompt&#xff1f; LLM大语言模型终究也只是一个工具&#xff0c;我们不可能每个人都去训一个大模型&#xff0c;但是我们可以思考如何利用好大模型&#xff0c;让他提升我们的工作效率。就像计算器工具一样&#xff0c;要你算10的10倍&#x…

今天看到一个有意思的问题:个人网站被恶意大量访问,怎么办(文末附GPT指令优化)

目录 问题描述 一、GPT 3.5 二、通义千问 三、讯飞星火 四、文心一言 五、Kimi 六、智谱清言 个人分析&#xff1a; 问题描述 大家好&#xff01;我的个人网站每天晚上7点30到11点被固定的十几个IP大量下载exe&#xff0c;造成网站带宽不够&#xff0c;怎么办! 已经把…

大模型系列之解读MoE

Mixtral 8x7B 的推出&#xff0c; 使我们开始更多地关注 基于MoE 的大模型架构&#xff0c; 那么&#xff0c;什么是MoE呢&#xff1f; 1. MoE溯源 MoE的概念起源于 1991 年的论文 Adaptive Mixture of Local Experts&#xff08;https://www.cs.toronto.edu/~hinton/absps/jjn…

如何在Android设备上恢复丢失的照片

Android手机或平板电脑上的照片丢失了&#xff1f;不要惊慌&#xff0c;您也许可以恢复它们。 由于我们的大量数据和日常生活都存储在一台设备上&#xff0c;有时将所有照片存储在本地的 Android 智能手机或平板电脑上可能是一项冒险的工作。无论是通过事故&#xff08;损坏、…

python数据分析常用基础语法

Python语言基础——语法基础 前言一、变量的介绍与使用变量的介绍变量命名规则变量的使用拓展 二、标识符标识符命名命名规则注意事项 三、数据类型数据类型的介绍数据类型的查看示例 四、输入与输出输入和输出的介绍format格式化输出占位符 五、代码缩进与注释代码缩进 前言 …

最高20K/月,安全、数通、云计算多个方向急招,可内推!

高级安全工程师【岗位职责及要求】 1、统筹负责行业客户的安全项目交付&#xff0c;能够独自输出技术方案并完成施&#xff0c;并具备指导初中级工程师实施的能力&#xff1b; 2、掌握H3C全系列安全产品功能并对全系列产品原理有深入了解&#xff0c;能够熟练完成安全产品规划及…

如何找到台式电脑的ip地址

在数字时代&#xff0c;每台接入网络的设备都拥有一个独特的标识&#xff0c;这就是IP地址。无论是手机、笔记本电脑还是台式电脑&#xff0c;IP地址都扮演着至关重要的角色&#xff0c;它帮助设备在网络世界中定位并与其他设备进行通信。对于许多电脑用户来说&#xff0c;了解…

RK3576芯片规格,以及与RK3588对比

瑞芯微RK3576是一款高性能、低功耗的SoC&#xff08;系统级芯片&#xff09;处理器&#xff0c;适用于基于ARM的PC、边缘计算设备、个人移动互联网设备等多种应用场景。它采用Arm架构的八核心CPU&#xff0c;集成了GPU、MCU、NPU、VPU等多种计算核心&#xff0c;并具有丰富的外…

Python面向对象编程思想的深入学习

魔术方法的使用 案例体验 class Student:def __init__(self, name, age):self.name nameself.age age# __str__魔术方法, 如果不去写这个方法&#xff0c;那么print输出的则是信息存储的内存地址。def __str__(self):return fStudent类对象&#xff0c;name:{self.name}, ag…