可利用空间表(Free List)
写这篇文章的动因是因为 2015 年 04 月 02 日的阿里在线笔试题考到了这个知识点。我当时模模糊糊的写了一些,估计写的也不对,所以在这里总结一下。
原题
常常会有频繁申请、释放内存的需求,比如在发送网络报文时,每次都要分配内存以存储报文,等报文发送完成后又需要删除报文。
为了避免频繁的new/delete对系统带来的开销,需要实现一个通用的FreeList机制。使用者总是从free list中分配内存,如果存在没有使用的内存块就直接摘出来使用,如果没有的话再从系统中分配。使用完毕后并不去直接delete该内存块,而是交给FreeList保管。
要求:
- 实现一个对固定大小内存块进行管理的通用FreeList类,给出定义和实现。要求不能使用STL中的容器类。定义类的接口和实现时注意通用性、健壮性和可测试性。
- 如果该类的对象可能会被多个thread同时访问,请描述如何怎样保证线程安全。有没有办法在保证线程安全的同时尽可能增大并发度?如果有也请描述你的思路。
一、介绍
“可利用空间表” 是动态内存管理的一种方法。通过把空闲内存划分成固定大小的数据块,而且利用指针字段把这些数据块链接起来,并使用一个指针指向首结点,这样就形成了一个单链表,即可利用空间表(free list)。
当用户请求分配时,系统从可利用空间表中删除一个结点分配之;当用户释放其所占内存时,系统即回收并将它插入到可利用空间表中,因此,可利用空间表亦称为“存储池”。
可利用空间表有三种结点结构:
结点大小相同:把内存分为大小相同的若干块,将各块链接起来,分配时从头上摘取,用完后插入到头上,这实际是链式栈。
结点有若干规格:当用户所需内存量不同,但只允许在几种规格间选取。这种情况下,可利用空间表中可以维护几条链表,同一链表中的结点大小相同。如大小为2、4、8字节,可以构造3个链表。
结点大小不等:内存块大小不固定,只有一个链表。通常操作系统的可利用空间表属于此类。即可利用空间表中只有一个大小为整个存储区的结点。随着分配和回收的进行,可利用空间表的结点大小和个数也随之而变化。—— 由于结点的大小不同,在分配时并不是可利用空间表中的任一结点都能满足,而需要按照申请的长度在可利用空间表中进行检索,找到其长度大于等于申请长度的结点,从中截取合适的长度。这就涉及到分配策略:
首次适配法:从链表头指针开始查找,找到第一个大于等于所需空间的结点即分配。(分配时查询,释放时插入表头)
最佳适配法:要求结点从小到大排列,找到第一个大于等于所需空间的结点即分配。(分配和回收时都需要查询)
最差适配法:要求结点从大到小排列,总从第一个结点开始分配。(分配时不需查询,回收时查询)
三种分配策略适合于不同的情况,首次适配法
的优点是速度快,缺点是可能把较大块拆分成较小的块,导致后来对大块的申请难以满足 —— 这种分配策略适合于系统事先不掌握运行期间可能出现的请求分配和释放的信息的情况。最佳适配法
的优点是使无法满足大请求块的可能性降到最低,但可能导致严重的外部碎片问题 —— 这种分配策略适合请求分配内存大小范围较广的系统。最差适配法
的优点是使得空闲块长度趋于一致,适合于分配请求长度比较均匀的情况。
二、C++实现
根据题目要求,实现一个对固定大小内存块进行管理的通用FreeList类,即结点大小相同。其实这是最简单的一种实现,注意几个实现要点:
一个静态成员指针static FreeList* freelist,用来指向可利用空间表。
重载 new 和 delete。
示例代码:
template <typename Elem>
class FreeList
{
private:
static FreeList<Elem> *freelist;
public:
Elem element;
FreeList *next;
FreeList(const Elem& elem, FreeList* next=NULL);
FreeList(FreeList* next=NULL);
void* operator new(size_t); // 重载new
void operator delete(void*); // 重载delete
};
template <typename Elem>
FreeList<Elem>* FreeList<Elem>::freelist = NULL;
template <typename Elem>
FreeList<Elem>::FreeList(const Elem& elem, FreeList* next)
{
this->element = elem;
this->next = next;
}
template <typename Elem>
FreeList<Elem>::FreeList(FreeList* next)
{
this->next = next;
}
template <typename Elem>
void* FreeList<Elem>::operator new(size_t)
{
/*freelist没有可用空间,就从系统分配*/
if(freelist == NULL)
return ::new FreeList;
/*否则,从freelist表头摘取结点*/
FreeList<Elem>* temp = freelist;
freelist = freelist->next;
return temp;
}
template <typename Elem>
void FreeList<Elem>::operator delete(void* ptr)
{
/*把要释放的结点空间加入到freelist中*/
((FreeList<Elem>*)ptr)->next = freelist;
freelist = (FreeList<Elem>*)ptr;
}
至于线程安全的问题,在多线程的环境下,线程同步的方式有多种:临界区、事件、互斥量、信号量。比如,我们可以把访问该类对象的代码段设置为 Critical Section,这样同一时间就只有一个线程可以执行这段代码。为了尽可能增大并发度,更好的方式是将代码改造成对临界数据的保护而不是对临界代码的保护,这样就可以令不会同时访问相同临界数据的线程完全并行地执行。
这是我个人的观点,如果你有更好的想法,欢迎交流和指正!
可利用空间表(Free List)的更多相关文章
- [20170623]利用传输表空间恢复数据库2.txt
[20170623]利用传输表空间恢复数据库2.txt --//继续上午的测试,测试truncate,是否可行,理论讲应该没有问题.我主要的目的测试是否要切换日志.--//参考链接 : http:// ...
- [20170623]利用传输表空间恢复部分数据.txt
[20170623]利用传输表空间恢复部分数据.txt --//昨天我测试使用传输表空间+dblink,上午补充测试发现表空间设置只读才能执行impdp导入原数据,这个也很好理解.--//这样的操作模 ...
- OpenCV学习笔记:如何扫描图像、利用查找表和计时
目的 我们将探索以下问题的答案: 如何遍历图像中的每一个像素? OpenCV的矩阵值是如何存储的? 如何测试我们所实现算法的性能? 查找表是什么?为什么要用它? 测试用例 这里我们测试的,是一种简单的 ...
- 空间表SpaceList
比如在建一个成绩管理系统,这时候定义的名字一般都是char szName[20],这样比较浪费,其实不只是定义名字,定义好多变量都这样,并没有体现动态. 此处出现空间表(SpaceList),通过指针 ...
- 1.2OpenCV如何扫描图像,利用查找表和计时
查找表 颜色缩减法:如果矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可有256个不同值. 但若是三通道图像,这种存储格式的颜色数就太多了(确切地说,有一千六百多万种).用如此之 ...
- 利用SQL表生成按日期序列的唯一ID
1. 创建一个表,用于存现在最大的ID SELECT [ID],[PreFix],[Code] FROM [DocumentNO] 2. 增加SP,利用锁表,生成相应的ID Create PROCED ...
- Oracle Spatial 创建空间表、添加空间原表信息、添加删除空间索引
一.创建空间表 创建一个空间表tbsvrc_buffer_t ,SQL如下: create table tbsvrc_buffer_t( ID VARCHAR2(50) not null, ...
- day-15 用opencv怎么扫描图像,利用查找表和计时
一.本节知识预览 1. 怎样遍历图像的每一个像素点? 2. opencv图像矩阵怎么被存储的? 3. 怎样衡量我们算法的性能? 4. 什么是查表,为什么要使用它们? 二.什么是查表,为什么要使 ...
- Oracle起步---创建临时表空间/表空间/创建用户/授权
1. 安装: 百度一下你就知道 2. sqlplus登录/sqlplus命令登录 在安装Oracle时,你需要记住设置的“全局数据库名”(默认为orcl) 和 口令,在以两种方式登录时: 用户名: s ...
随机推荐
- swift potocol 作为参量时函数的派发顺序
1.检查protocol本体是否声明调用函数: 2.如果没有,检查protocol扩展是否有该函数:如果扩展中也没有,报错: 3.如果本体声明了函数,使用动态派发机制进行派发:扩展中的实现位于最末位.
- swift 与 NSObject
以NSObject为基类,只是为了提供Objective-C API的使用入口: 经由@object修改的对象,是这些api的参量. NSObject是swift与oc特有机制沟通的桥梁. Subcl ...
- sosoapi的安装
sosoapi简介及其用户手册:http://www.sosoapi.com/pass/help/manual.htm 该随笔的大概分为: 1.sosoapi的基础安装 2.sosoapi使用域名访 ...
- pycharm acejumpchar插件
1链接:https://plugins.jetbrains.com/plugin/7163-emacsideas 2将其pycharm路径下:\.PyCharm2017.1\config\plugin ...
- U盘启动盘制作工具(安装Linux)
2018-09-15 17:36:42 1. Etcher 官网:https://etcher.io/ 资料来源:https://linuxmint-installation-guide.readt ...
- loaction.reload(false)和location.reload(true) js发起请求
loaction.reload(false)和location.reload(true)差别: loaction.reload(false) 先判断页面有没修改,有的话就从服务器下载页面,没有就直接从 ...
- //……关于HTTP与HTTPS
图1 图2 第一张访问域名http://www.tsinghua.edu.cn,谷歌浏览器提示不安全链接,第二张是https://www.12306.cn/index,浏览器显示安全,为什么会这样子呢 ...
- 九度oj 题目1023:EXCEL排序
题目1023:EXCEL排序 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:20699 解决:4649 题目描述: Excel可以对一组纪录按任意指定列排序.现请你编写程序实现类似 ...
- [luoguP1022] 计算器的改良(模拟)
传送门 超级大模拟.. 代码 #include <cstdio> #include <cstring> #include <iostream> #define is ...
- 关于iphone 微信浏览器编码问题
这个问题最终没有完美的解决,给出的一个解决方法是返回一个html文档.