C语言动态内存管理
动态存储管理的基本问题是:系统如何按请求分配内存,如何回收内存再利用。提出请求的用户可能是系统的一个作业,也可能是程序中的一个变量。
空闲块
未曾分配的地址连续的内存区称为“空闲块”。
占用块
已分配给用户使用的地址连续的内存区称为“占用块”。
系统刚刚启动时,整个内存可看做一个大的“空闲块”,随着用户请求的进入,系统依次分配相应的内存。
在系统运行过程中,内存被分为两大部分:低地址区(若干占用块)和高地址区(空闲块)。
经过一段时间后,有的程序运行结束,释放掉它所占用的内存,使之变为空闲块,这就使整个内存中空闲块和占用块之间出现了相互交错的现象。如下图:
当系统进入到图中b),又有新的用户请求分配内存时,系统该如何处理呢?
方法一:系统继续从高地址区的空闲块进行分配,直到无法分配。当剩余的空闲块不能满足分配请求时,系统才去回收所有的不再使用的内存区,并重新组织内存,紧凑所有的空闲块为一个大的空闲块,以备在分配。
方法二:空闲链表。空闲链表中包含了所有空闲块的信息,一个节点对应一个空闲块。当用户请求分配时,系统所作的工作就是搜索空闲链表,按某种策略找到一个合适的空闲块进行分配,并删除对应的节点。当用户释放所占用的内存时,系统回收该内存,并将它插入到空闲链表中。
空闲链表三种结构形式:
(1)所有请求的内存大小相同。这是一种最简单的动态存储管理方式。
对此,系统通常的做法是:
a)系统启动时,将内存按大小均分成若干个块,并形成一个链表。
b)分配时,只需将链表中第一个节点分配给用户即可,无需扫描整个链表。
c)回收时,将空闲块插入到链表头即可。
(2)所有请求的内存大小有n种规格。可创建n个(1)情况中的链表,分配与回收与(1)类似,不同之处在于:当请求的链表为空时,需要在节点大的链表上进行分配,取一部分内存给用户,剩余部分作为一个新节点插入到相应的链表中。
(3)所有请求的内存大小是不同的,是不断发生变化的。这种情况下的分配与回收见下篇。
对于内存块大小不同的空闲链表,假如有一个大小为N的内存申请,如果空闲链表中大于N的内存只有一块,假如大小为M,就把M的一部分分配给用户,同时把剩余的M-N的部分作为一个节点插入到空闲链表中。
当大于N的内存有多块时,我们该如何分配呢?通常有以下三种方法:
(1)最先适配法。顾名思义,顺序扫描空闲链表,找到第一个大于等于N的空闲块,把其中的N分配给用户。因为链表不按内存的地址有序,也不按大小有序,所以回收内存块放到表头即可。
问题:运行一段时间后,链表中将会出现一些非常小的块,并在这些小块都在链表的前边。
(2)最优适配法。在空闲链表中找到一个不小于N并且最接近N的空闲块分配给用户。分配时需要整表扫描。如果链表按空闲块从小到大有序,分配时只需找到第一个大于N的空闲块,回收时需要插入到链表合适的位置上。
(3)最差适配法。将链表中不小于N且是链表中最大的空闲块的一部分分配给用户。链表可按空闲块从大到小的顺序排列,分配只需判断链表第一个空闲块即可,回收时需要插入到链表合适的位置上。
对于回收,我们考虑的不仅仅是归还节点,还要考虑空闲块的合并问题。这种把物理地址相邻的空闲块的合并以取得更大的空闲块的方法是需要的。空闲链表中的空闲块并不是按物理地址有序的,那又如何快速合并呢?下篇中将讨论一种方法:边界标识法
原文链接:http://blog.csdn.net/hbuxiaoshe/article/details/5994743
就空闲链表来说,要确定哪些空闲块是物理地址相邻的,确实不太容易,需要整表扫描。
边界标识法能够有效的判断空闲块的物理相邻内存块是否为空闲块。
所有的空闲块组成一个双向循环链表,链表中每一个节点的结构如下:
llink和rlink分别指向链表中的前驱后继节点。
size表明内存块的大小。
uplink指向本块的首地址,图中箭头,其作用下面讲述。
tag头和尾中各有一个,作为内存块边界的标识,0表空闲块,1表占用块。
分配时同样会在链表上产生很多非常小的空闲块,影响分配的效率,因此,可以定义一个全局的指针p,当不为空时分配,使其始终指向刚进行过分配的节点的后继节点。
回收时判断物理相邻的块是否为空闲块的方法:
假设当前回收块的物理地址为p(下面涉及到的左邻块和右邻块都是物理地址相邻的,在循环链表中也许不相邻),
左邻块:左邻块的尾的地址是p1=p-sizeof(尾),左邻块是否为空闲块的判断则为p1->tag是否为0。当为空闲块时,只需将当前块合并到左邻块中即可,而左邻块的地址则需要由uplink来获得(原因是中间内存块的大小是不固定的,不能通过偏移来实现),即p1->uplink。修改左邻块的size和uplink即可。
右邻块:右邻块的首的地址为p2=p+sizeof(头),右邻块是否为空闲块的判断则为p2->tag是否为0。当为空闲块时,与右邻块合并,修改p的llink、rlink和size,修改p2的uplink。
你可能会认为,既然链表中的块都是空闲块,干吗还要加上tag标识位呢?我的回答是,如果没有tag,当我们找到右邻块的地址时,没有办法判断右邻块是不是链表中的一个节点,也许它还在被用户使用着。那为什么要用两个tag呢?个人觉得,用一个也可以,但用两个更容易判断。
C语言动态内存管理的更多相关文章
- C++中对C的扩展学习新增语法——动态内存管理
1.C语言动态内存管理的缺点: 1.malloc对象的大小需要自己计算. 2.需要手动转换指针类型. 3.C++的对象不适合使用malloc和free. 2.C++中new/delete基本使用: 3 ...
- C语言之动态内存管理
C语言之动态内存管理 大纲: 储存器原理 为什么存在动态内存的开辟 malloc() free() calloc() realloc() 常见错误 例题 柔性数组 零(上).存储器原理 之前我们提到了 ...
- JVM内存管理------JAVA语言的内存管理概述
引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓 ...
- C语言的内存管理
C语言的内存管理 转载:http://blog.csdn.net/wind19/article/details/5964090 对于一个C语言程序而言,内存空间主要由五个部分组成代码段(.text ...
- FreeRTOS 动态内存管理
以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解 FreeRTOS 动态内存管理,动态内存管理是 FreeRTOS 非常重要的一项功能,前面 ...
- JVM内存管理之JAVA语言的内存管理概述
引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓 ...
- 动态内存管理详解:malloc/free/new/delete/brk/mmap
c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, calloc/realloc 上述8个函数/操作符是c/c++语言里常用来做动 ...
- 字符串输出输入函数,const修饰符,内存分区,动态内存管理,指针和函数,结构体
1.字符串输出输入函数 读入字符串的方法: 1) scanf 特点:不能接收空格 2) gets 特点:可以接受含有空格的字符串 ,不安全 3) fgets(); 特点:可以帮我们自动根据数组的长度截 ...
- 动态内存管理---new&delete
动态内存管理 动态对象(堆对象)是程序在执行过程中在动态内存中用new运算符创建的对象. 因为是用户自己用new运算符创建的.因此也要求用户自己用delete运算符释放,即用户必须自己管理动态内存. ...
随机推荐
- ios--socket
一.打开服务器 a.在终端打开,到服务器文件路径输入命令 python chatserver.py b.当显示 Iphone Chat server started 表示成功 二.建立连接 a.设置对 ...
- C# ACM poj1002
排序 public static void acm1002(string[] azx) { string[] a = new string[azx.Length]; ; i < azx.Leng ...
- 我的VIM.rc
我的VIM.rc """""""""""""""& ...
- nginx 默认会把header里的参数去掉下划线
做token验证的时候遇到问题:在本地可以获取前端header传的参数,但是部署到服务器获取的就是null(服务器地址用nginx做了代理) 原因: nginx代理默认会把header的参数的 &qu ...
- ThreadPoolExecutor 线程池的实现
ThreadPoolExecutor继承自 AbstractExecutorService.AbstractExecutorService实现了 ExecutorService 接口. 顾名思义,线程 ...
- Openstack安装
作者:陈沙克 Openstack发展很猛,很多朋友都很认同,2013年,会很好的解决OpenStack部署的问题,让安装,配置变得更加简单易用. 很多公司都投入人力去做这个,新浪也计划做一个Opens ...
- .Net 中资源的使用方式
近期要在小丸工具箱中添加一个启动画面,画面中需要使用一个GIF动图.经过学习和实验,总结了几个读取资源的方式,罗列如下. 一.使用外部资源 Image img = Image.FromFile(&qu ...
- 重学C语言 -- printf,scanf
printf(); 用来显示格式串的内容 注意: 参数不可以换行,否则会出一个警告. 格式串中占位符比表达式数量多 会显示一个无意义值 格式串中占位符比表 ...
- Android开发第2篇 - Git插件安装
一.Eclipse上安装GIT插件EGit Eclipse的版本eclipse-java-helios-SR2-win32.zip(在Eclipse3.3版本找不到对应的 EGit插件,无法安装) E ...
- Python设计模式——装饰模式(Decorator)
假如我们需要开发一个程序来展示一个人穿衣服的过程. #encoding=utf-8 __author__ = 'kevinlu1010@qq.com' class Person(): def __in ...