写这篇文章的动因是因为 2015 年 04 月 02 日的阿里在线笔试题考到了这个知识点。我当时模模糊糊的写了一些,估计写的也不对,所以在这里总结一下。

原题

常常会有频繁申请、释放内存的需求,比如在发送网络报文时,每次都要分配内存以存储报文,等报文发送完成后又需要删除报文。

为了避免频繁的new/delete对系统带来的开销,需要实现一个通用的FreeList机制。使用者总是从free list中分配内存,如果存在没有使用的内存块就直接摘出来使用,如果没有的话再从系统中分配。使用完毕后并不去直接delete该内存块,而是交给FreeList保管。

要求:

  1. 实现一个对固定大小内存块进行管理的通用FreeList类,给出定义和实现。要求不能使用STL中的容器类。定义类的接口和实现时注意通用性、健壮性和可测试性。
  2. 如果该类的对象可能会被多个thread同时访问,请描述如何怎样保证线程安全。有没有办法在保证线程安全的同时尽可能增大并发度?如果有也请描述你的思路。

一、介绍

“可利用空间表” 是动态内存管理的一种方法。通过把空闲内存划分成固定大小的数据块,而且利用指针字段把这些数据块链接起来,并使用一个指针指向首结点,这样就形成了一个单链表,即可利用空间表(free list)

当用户请求分配时,系统从可利用空间表中删除一个结点分配之;当用户释放其所占内存时,系统即回收并将它插入到可利用空间表中,因此,可利用空间表亦称为“存储池”。

可利用空间表有三种结点结构:

  1. 结点大小相同:把内存分为大小相同的若干块,将各块链接起来,分配时从头上摘取,用完后插入到头上,这实际是链式栈。

  2. 结点有若干规格:当用户所需内存量不同,但只允许在几种规格间选取。这种情况下,可利用空间表中可以维护几条链表,同一链表中的结点大小相同。如大小为2、4、8字节,可以构造3个链表。

  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,这样同一时间就只有一个线程可以执行这段代码。为了尽可能增大并发度,更好的方式是将代码改造成对临界数据的保护而不是对临界代码的保护,这样就可以令不会同时访问相同临界数据的线程完全并行地执行。

这是我个人的观点,如果你有更好的想法,欢迎交流和指正!

个人站点:http://songlee24.github.com

可利用空间表(Free List)的更多相关文章

  1. [20170623]利用传输表空间恢复数据库2.txt

    [20170623]利用传输表空间恢复数据库2.txt --//继续上午的测试,测试truncate,是否可行,理论讲应该没有问题.我主要的目的测试是否要切换日志.--//参考链接 : http:// ...

  2. [20170623]利用传输表空间恢复部分数据.txt

    [20170623]利用传输表空间恢复部分数据.txt --//昨天我测试使用传输表空间+dblink,上午补充测试发现表空间设置只读才能执行impdp导入原数据,这个也很好理解.--//这样的操作模 ...

  3. OpenCV学习笔记:如何扫描图像、利用查找表和计时

    目的 我们将探索以下问题的答案: 如何遍历图像中的每一个像素? OpenCV的矩阵值是如何存储的? 如何测试我们所实现算法的性能? 查找表是什么?为什么要用它? 测试用例 这里我们测试的,是一种简单的 ...

  4. 空间表SpaceList

    比如在建一个成绩管理系统,这时候定义的名字一般都是char szName[20],这样比较浪费,其实不只是定义名字,定义好多变量都这样,并没有体现动态. 此处出现空间表(SpaceList),通过指针 ...

  5. 1.2OpenCV如何扫描图像,利用查找表和计时

    查找表 颜色缩减法:如果矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可有256个不同值. 但若是三通道图像,这种存储格式的颜色数就太多了(确切地说,有一千六百多万种).用如此之 ...

  6. 利用SQL表生成按日期序列的唯一ID

    1. 创建一个表,用于存现在最大的ID SELECT [ID],[PreFix],[Code] FROM [DocumentNO] 2. 增加SP,利用锁表,生成相应的ID Create PROCED ...

  7. Oracle Spatial 创建空间表、添加空间原表信息、添加删除空间索引

    一.创建空间表 创建一个空间表tbsvrc_buffer_t ,SQL如下: create table tbsvrc_buffer_t(  ID      VARCHAR2(50) not null, ...

  8. day-15 用opencv怎么扫描图像,利用查找表和计时

    一.本节知识预览 1.  怎样遍历图像的每一个像素点? 2.  opencv图像矩阵怎么被存储的? 3.  怎样衡量我们算法的性能? 4.  什么是查表,为什么要使用它们? 二.什么是查表,为什么要使 ...

  9. Oracle起步---创建临时表空间/表空间/创建用户/授权

    1. 安装: 百度一下你就知道 2. sqlplus登录/sqlplus命令登录 在安装Oracle时,你需要记住设置的“全局数据库名”(默认为orcl) 和 口令,在以两种方式登录时: 用户名: s ...

随机推荐

  1. python_MachineLearning_感知机PLA

    感知机:线性二类分类器(linear binary classifier)   感知机(perceptron)是二类分类的线性模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值.感知机对 ...

  2. 转:谈谈iOS中粘性动画以及果冻效果的实现

    在最近做个一个自定义PageControl——KYAnimatedPageControl中,我实现了CALayer的形变动画以及CALayer的弹性动画,效果先过目: 先做个提纲: 第一个分享的主题是 ...

  3. windows测试物理网络

    ping 192.168.10.88 -t ,参数-t是等待用户去中断测试 

  4. 梦想CAD控件,用于浏览和编辑DWG文件,在脱离AUTOCAD的情况下独立运行,相当于简易CAD

    (百度百科连接) 梦想绘图控件5.2  是国内最强,最专业的CAD开发组件(控件),不需要AutoCAD就能独立运行.控件使用VC 2010开发,最早从2007年第一个版本完成,经过多年的累积已经非常 ...

  5. Ubuntu16.04 python3.4.3升级到python3.7.1

    python有两个版本,一个2版本,使用的是python:另一个是3版本,使用的是python3. 简易安装python后得到的3版本的版本号是python3.4.3. 可以使用下面的命令查看py版本 ...

  6. css的存在形式

    1.css的样式,可以写在head头中: 1).通过ID(#CC{}) 2).通过class (.cc{}) 2.可以将样式,单独写入css的某一个页中 1)通过在head头中,引改该css样式,通过 ...

  7. Python学习笔记(1)——Python的概述(Python的环境、变量、数据类型、基本运算)

    Table of Contents 1. Python概述 1.1. Python基础知识 1.2. 运行环境 1.3. Python的格式 1.4. Python的变量. 2. Python的数据类 ...

  8. BZOJ 2055 80人环游世界 有上下界最小费用可行流

    题意: 现在有这么一个m人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家.    因为他们主要分布在东方,所以他们只朝西方进军.设从东方到西方的每一个国家的编号依次为1...N.假若第 ...

  9. HDU - 2102 A计划(双层BFS)

    题目: 可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸的她再一次面临生命的考验.魔王已经发出消息说将在T时刻吃掉公主,因为他听信谣言说吃公主的肉也能长生不老.年迈的国王正是心急如焚, ...

  10. 笔试算法题(17):奇偶数分置数组前后段 & 反序访问链表

    出题:输入一个数组,要求通过交换操作将奇数索引的元素调整到数组前半部分,偶数索引的元素调整到数组后半部分: 分析: 当然如果没有额外要求的话很容易实现,最好使用In-Place的实现策略:考虑插入排序 ...