虽然大部分系统都有提供内存动态分配和释放函数(即C语言中的malloc和free函数),但是在嵌入式开发中由于系统的限制往往需要自己来实现内存管理,如在有些平台上可动态申请的最大空间不能满足程序设计的需要,有些系统提供的内存分配和释放函数会造成大量的内存碎片导致内存不够用,在这些时候往往就需要自己先申请一块较大的内存,然后在这个较大的内存中进行重新分配,即做一套独立的内存管理程序。其实自己设计一个内存管理程序有如下几个好处:
1、便于对程序在运行中对内存的使用情况进行监测,如是否有内存泄漏,内存使用的峰值等;
2、防止因程序设计不当(如指针越界、野指针)造成对系统的破坏,便于界定问题出现的范围;
3、通过优化内存管理算法可以自行减少内存碎片;
4、便于程序移植,在程序移植到不同平台时可以不用考虑系统的内存管理情况,只须考虑能否得到大块内存即可。
关于内存的分配和释放方法也许有很多方法可以采用,这里提出一个利用二叉树的思想来管理内存。二叉树是一种常用的数据结构,被运用到很多种算法中,二叉树的定义在这里就不再做赘述了,其从形态上可以看出二叉树主要特点是:二叉树中每一个结点最多有两个子结点且最多有一个父结点。内存的分配即是对一个较大的内存块分割成几个较小的内存块,一般采用的方法是以2的幂次作为块的大小(即内存粒度),当要分配的内存大小不等于所设定的内存粒度时则找一个最靠近这个大小且比它大的粒度来分配,这种分配方法完全可以用一个二叉树的形式来表示,如下图:
上图是从一个1KB的内存空间中分配出64字节的情况,叶子结点表示被分割出来的内存块,当要查找一块空闲的内存块时,应当是在这些叶子结点中查找,如果找不到,则找一个较大一点的空闲结点进行再分配。
运用二叉树的思想主要目的是用于理解空闲空间的回收方法,内存的不断分配和释放势必会造成大量的内存碎片,如果不对内存碎片进行整理最终会导致无内存可用,所以在释放(free)内存时要把连续的内存空间合并起来,以免有内存碎片,但并不是所有的连续空间都适宜合并,如下图:
内存块h、i、j、k为连续空间,其中h和k空间为已被使用的空间,i和j空间是刚被释放出来的空闲空间,显然,空闲空间i和j可以组成一个更大的如128B大小的空闲空间,但是这样做的结果是不仅破坏了二叉树原有的思想也造成以后若要合并空间h和k会比较复杂,会增加很多算法,如要判断空间i和j是否已合并,而且每释放一个内存空间都要对每层的二叉树进行这种类似的判断,虽然这样可以更加减少内存碎片,但是释放内存函数是个常用的函数,这种计算量特别是在嵌入式开发中是无法承受的,而且出现上面的这种情况在程序中一般较少出现,所以我们对于这样的连续空间不进行合并,只有对是同一个父结点的两个叶子结点才进行合并。
总的思想是:对内存进行切割时,总是把一块大的内存空间切割成两块小的内存空间,对内存进行合并时,只把原来都是由同一个内存块分割出来的两块内存块进行合并
下面就根据这样的思想来实现内存申请和释放方法:
1. 数据结构定义
用一个数组定义内存粒度(以2的倍数来定义):
#define GRANULARITY_NUMBER 12   
const int MemGranularity [GRANULARITY_NUMBER] =
{4,8,16,32,64,128,256,512,1024,2048,4096,0xffff};
建立一个以粒度来分类的内存块指针数组(内存桶):
typedef struct mem_block_info_struct
{
void* memptr;                    //内存块指针
mem_block_info_struct* nextblock;//指向后一个内存块的指针
}MemBlockInfo;
typedef struct mem_array_head_struct
{
MemBlockInfo* freedmemarray;//指向空闲内存块链表的指针
MemBlockInfo* usedmemarray;//指向已用内存块链表的指针
}MemArrayHead;
MemArrayHead MemBucket[GRANULARITY_NUMBER] ={ };
如MemBucket [0]是指向内存块大小为4字节的内存块链表,MemBucket [1]则是指向内存块大小为8字节的内存块链表,依此类推。
可以在大块内存前面划出一部分内存来存储每个内存块的信息,如下图:

 这里就必须要预先估计要划分出多大的内存来存储每个内存块的信息,对于较小的运行程序比较容易估计,但是如果程序较复杂则要通过实验来确定划分大小,可以事先设定一个宏定义值,以后只要改这个宏定义值就可以了,如:

#define MEMORY_BLOCK_MAX_NUMBER 2048
MemBlockInfo* MemBlockArray = NULL;
MemBucket指向的内存块链表都按内存块指针(memptr)值进行由低到高的排序,其实只要在插入一个新内存块时按指针值大小有序插入就可以了,这样做以便于查找链表中的结点,以提高查找效率。
2. 算法实现
1)、初始化方法
a. 初始化全局变量;
b. 在内存块前部分出一部分用来存储内存块信息;
c. 从剩余的内存空间开始按设定的内存粒度对内存进行切割,切割原则是使得切割出来的内存块总数为最小值,方法是按粒度由大到小进行切割,假设有剩余空间大小为7680B,则可切割出内存块有:4096B、2048B、1024B、512B(这意味着我们先建立4个只有一个结点的二叉树),这些内存块的指针存到MemBlockArray中,然后根据粒度大小和内存块大小把MemBucket相应指针指到MemBlockArray中。
2)、动态分配内存方法(即malloc函数实现方法)
a. 根据传入参数中指定的大小来决定要分配多大的内存粒度,当分配大小刚好等于某个粒度大小时,则按该粒度大小来分配,如果找不到相应大小粒度,则找一个比要分配的空间大小要大的最小粒度作为分配空间,例如要分配20字节空间,则分配一个粒度为32字节的空间。
b. 根据粒度大小在空闲链表中寻找相应空闲空间:若找到则返回该空闲空间指针,并把相应结点移入到已用链表中;若没有找到,则找一个粒度更大一点的空闲块,然后把该空闲块进行一半一半地分割,直到分割出得到所要的粒度大小为止,最后把被分割的空闲块从链表中移出,并把该结点的指针都置为NULL,分割出来的空闲块指针都存到MemBlockArray中,MemBlockArray中新增的数组单元内容作为结点添加到MemBucket空闲链表中(按指针大小顺序来添加),取分割出来的其中一个最小的空闲块作为要返回的内存块,并把这个内存块移入到已用链表中。
3)、释放内存块方法(即free函数实现方法)
搜索所有的已用链表,找出与传入的指针大小一样的结点,把该结点从已用链表中移出到空闲链表中,也是按顺序插入到空闲链表中,如果发现相邻结点为连续空间,而且相邻结点都原来为从同一个较大的空闲内存块分割出来的(即为同一个父结点的两个子结点),则合并这两个内存块,把合并后的内存块移入到更大粒度的空闲链表中,如果在更大的粒度空闲链表中又发现有可合并内存块,则再进行合并,依此方法,直到不能合并为止。
合并空闲内存块方法:把这两个内存块结点都从空闲链表中移出,按内存地址大小,把较小地址指针的结点移到更大粒度的空闲链表中,把较大地址指针的结点中所有指针值都置为NULL。
判断两个内存块是否是从同一个较大的空闲内存块分割出来的方法:如下图
如果内存块1和内存块2都是从同一个较大的空闲内存块分割出来的,则满足如下条件:
(前面内存块+内存块1)/内存块1的大小:这个结果为奇数
(前面内存块+内存块1+内存块2)/内存块2的大小:这个结果为偶数
3.该方法优缺点
1)、优点:实现方法较简单,产生的内存碎片较少。
2)、缺点:内存空间利用率不高,当内存分配中需要较多内存块时,由于要存储内存块信息,要浪费较大的内存空间,而且存储内存块信息的数组长度为固定大小,则在具体应用中可能需要调节该大小,较不灵活。

利用二叉树的思想来实现分配和释放内存方法相关推荐

  1. 数据结构上机实践第九周项目3 - 利用二叉树遍历思想解决问题

    利用二叉树遍历思想解决问题 学以致用,知行合一,学了知识就要会运用,否则跟背课文没什么区别,上次实践,做了二叉树递归遍历的算法实现,本次实践,将利用遍历思想解决问题,将遍历思想真正的运用到实际问题需求 ...

  2. 第九周项目实践3 利用二叉树遍历思想解决问题

    *Copyright (c)2017,烟台大学计算机与控制工程学院 *All rights reserved. *文件名称: *作 者:邵雪源 *完成日期:2017年11月2日 *版 本 号:v1.0 ...

  3. 为指针分配和释放空间

    第二十章 指针 二 为指针分配和释放空间http://17de.com/library/CPP/ls20.htm#20.5.2 20.1 理解指针的两种"改变" 20.1.1 改变 ...

  4. 第二十章 指针 二 为指针分配和释放空间(转)

    载自<白话c++>:http://17de.com/library/CPP/ls20.htm 20.1 理解指针的两种"改变" 20.1.1 改变指针的值 20.1.2 ...

  5. ALLOCATE语句分配FORTRAN动态数组方法

    数组的动态分配 a)    可分配数组 数组可以是静态的也可以是动态的.如果数组是静态的,则在编译时就被分配了固定的储存空间,并且直到程序退出时才被释放.程序运行时静态数组的大小不能改变.静态数组的缺 ...

  6. linux系统分配文件夹内存,详解Linux系统内存知识及调优方案

    内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁.计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大.内存作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器 ...

  7. 指针分配和释放空间(转)

    指针分配和释放空间(转) (2012-06-06 12:42:04) 转载▼ 标签: 指针 分类: C/Cplusplus 20.1 理解指针的两种"改变" 普通变量(非指针,简单 ...

  8. delphi.memory.分配及释放---New/Dispose, GetMem/FreeMem及其它函数的区别与相同

    我估摸着内存分配+释放是个基础函数,有些人可能没注意此类函数或细究,但我觉得还是弄明白的好. 介绍下面内存函数前,先说一下MM的一些过程,如不关心可忽略: TMemoryManager = recor ...

  9. C#基础第七天-作业-利用面向对象的思想去实现名片-动态添加

    1.利用面向对象的思想去实现: (增加,修改,删除,查询,查询全部) 需求:根据人名去(删除/查询). 指定列:姓名,年龄,性别,爱好,电话. 多条添加 , 动态添加 名片 本系列教程: C#基础总结 ...

最新文章

  1. 802.1x------2
  2. 2018python培训-2018python深度学习核心技术培训班
  3. mysql排序时设置主次_Mysql实现Rownum()排序后根据条件获取名次
  4. 服装零售行业洞察报告
  5. (42)Xilinx FIFO IP核配置(三)(第9天)
  6. 如何使用PSDatabaseClone设置基于图像SQL Server数据库配置
  7. MapReduce 支持的部分数据挖掘算法
  8. ironpython怎么编译_将IronPython WPF项目编译为
  9. Linux QQ 2.0 Beta版初体验
  10. 斐讯路由器k3c虚拟服务器,斐讯K3C路由器32.1.26.175如何打开telnet升级到官改固件教程...
  11. 如何在高共模电压下测量小差分电压
  12. 如何用阿里云服务器建站-全流程
  13. 白话前沿IT技术系列—云计算
  14. 09开博——不差钱,就缺朋友
  15. 斯坦福大学计算机科学专业硕士排名,斯坦福大学计算机系统研究生排名关键的都看过来...
  16. c mysql创建索引,如何创建mysql索引
  17. 5G网络的关键技术及特点,面临的挑战!
  18. 中专计算机基础知识汇总,【职业中专计算机基础教育分析】 计算机基础知识...
  19. 第904题 水果成篮
  20. Java 爬虫微信公众号详情,并且破解微信图片跨域问题

热门文章

  1. java学习记录--ThreadLocal使用案例
  2. Hibernate 系列教程9-自关联
  3. Visual Studio 中粗略的代码行数统计
  4. Openlayers 2.X加载高德地图
  5. 2.5 网络中的网络及1x1卷积-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  6. 4.9 总结-深度学习第一课《神经网络与深度学习》-Stanford吴恩达教授
  7. 5.8 程序示例--线性分类-机器学习笔记-斯坦福吴恩达教授
  8. 一起学nRF51xx 7 -  spi
  9. 基于卷积神经网络的人脸认证(判断两个人脸是否是一个人)
  10. ubuntu18.04安装VCS+verdi错误集锦