用户请求系统分配内存:(两种策略)
1)系统继续从高地址的空闲块中进行分配,不理会已分配的内存区是否已经空闲,直到无法满足分配请求时,系统再去回收用户不再使用的空闲块。
2)用户执行完结束,立马分配过的空闲块释放,用户请求分配时,遍历整个内存区,找合适空闲块分配。
可利用空间表实现内存分配方法:
表中包含所有可分配的空闲块,每一块是链表中一个节点。
1)所有用户请求分配存储量大小相同。
2)分配的存储量分为不同规格。如2,4,8字节建立3,5,9字的链表。
3)分配给用户请求内存块大小不固定,可以随需求改变。
其中当用户请求的内存块大小不固定时,对于寻找空闲块的分配策略也不同:
1》首次拟合法。找到第一个不小于n的空闲块,分配给用户,剩下一部分空闲块继续留在表中。(分配时查找表。)
2》最佳拟合法。找到表中不小于n最接近n的分配给用户。(为了避免遍历整个表,将空闲块大小由小到大排序。空闲块大小差别两极分化,分配回收都需查找链表。)
3》最差拟合法。将表中不小于n并且最大的空闲块的一部分分配给用户。(同2,将空闲块由大到小排序。回收时查找。)
边界标识法:(内存大小不固定的情况)
双向循环链表结构。
回收算法:
当用户释放占用块后,系统会回收进行再分配,使物理地址上的空闲块尽可能大,实现了以下机制;
若释放块左右为占用块,释放块作为节点插入到表中。若左空右占则将释放块与左邻区合并为一新节点,左占右空相同。左右皆空则将左右与释放块合并。
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define FootLoc(p) p+p->size-1
typedef int Status; // Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int Boolean; // Boolean是布尔类型,其值是TRUE或FALSE
typedef struct WORD // 字类型
{
union
{
WORD *llink; // 头部域,指向前驱结点
WORD *uplink; // 底部域,指向本结点头部
};
int tag; // 块标志,0:空闲,1:占用,头部和尾部均有
int size; // 头部域,块大小
WORD *rlink; // 头部域,指向后继结点
}WORD,head,foot,*Space; // *Space:可利用空间指针类型
Space AllocBoundTag(Space &pav,int n) // 算法8.1(首次拟合法)
{ // 若有不小于n的空闲块,则分配相应的存储块,并返回其首地址;否则返回NULL
// 若分配后可利用空间表不空,则pav指向表中刚分配过的结点的后继结点
Space p,f;
for(p=pav;p&&p->size<n&&p->rlink!=pav;p=p->rlink); // 查找不小于n的空闲块
if(!p||p->size<n) // 找不到,返回空指针
return NULL;
else // p指向找到的空闲块
{
f=FootLoc(p); // 指向底部
pav=p->rlink; // pav指向*p结点的后继结点
if(p->size-n<=e) // 整块分配,不保留<=e的剩余量
{
if(pav==p) // 可利用空间表变为空表
pav=NULL;
else // 在表中删除分配的结点
{
pav->llink=p->llink;
p->llink->rlink=pav;
}
p->tag=f->tag=1; // 修改分配结点的头部和底部标志
}
else // 分配该块的后n个字(高地址部分)
{
f->tag=1; // 修改分配块的底部标志
p->size-=n; // 置剩余块大小
f=FootLoc(p); // 指向剩余块底部
f->tag=0; // 设置剩余块底部
f->uplink=p;
p=f+1; // 指向分配块头部
p->tag=1; // 设置分配块头部
p->size=n;
}
return p; // 返回分配块首地址
}
}
void Reclaim(Space &pav,Space &p)
{ // 边界标识法的回收算法
Space s=(p-1)->uplink,t=p+p->size; // s、t分别指向释放块的左、右邻块(空闲时)的首地址
int l=(p-1)->tag,r=(p+p->size)->tag; // l、r分别指示释放块的左、右邻块是否空闲
if(!pav) // 可利用空间表空
{ // 将释放块加入到pav所指的可利用空间表中
pav=p->llink=p->rlink=p; // 头部域的两个指针及pav均指向释放块
p->tag=0; // 修改头部域块标志为空闲
(FootLoc(p))->uplink=p; // 修改尾部域
(FootLoc(p))->tag=0;
}
else // 可利用空间表不空
{
if(l==1&&r==1) // 左右邻区均为占用块
{
p->tag=0; // 修改头部域块标志为空闲
(FootLoc(p))->uplink=p; // 修改尾部域
(FootLoc(p))->tag=0;
pav->llink->rlink=p; // 将p所指结点(刚释放的结点)插在pav所指结点之前
p->llink=pav->llink;
p->rlink=pav;
pav->llink=p;
pav=p; // 修改pav,令刚释放的结点为下次分配时的最先查询的结点
}
else if(l==0&&r==1) // 左邻区为空闲块,右邻区为占用块
{ // 合并左邻块和释放块
s=(p-1)->uplink; // 左邻空闲块的头部地址
s->size+=p->size; // 设置新的空闲块大小
t=FootLoc(p); // 设置新的空闲块底部
t->uplink=s;
t->tag=0;
}
else if(l==1&&r==0) // 右邻区为空闲块,左邻区为占用块
{ // 合并右邻块和释放块
p->tag=0; // P为合并后的结点头部地址
p->llink=t->llink; // p的前驱为原t的前驱
p->llink->rlink=p; // p的前驱的后继为p
p->rlink=t->rlink; // p的后继为原t的后继
p->rlink->llink=p; // p的后继的前驱为p
p->size+=t->size; // 新的空闲块的大小
(FootLoc(t))->uplink=p; // 底部(原t的底部)指针指向新结点的头部
if(pav==t) // 可利用空间表的头指针指向t(t已不是空闲结点首地址了)
pav=p; // 修改pav,令刚释放的结点为下次分配时的最先查询的结点
}
else // 左右邻区均为空闲块
{
s->size+=p->size+t->size; // 设置新结点的大小
t->llink->rlink=t->rlink; // 删去右邻空闲块结点
t->rlink->llink=t->llink;
(FootLoc(t))->uplink=s; // 新结点底部(原t的底部)指针指向其头部
if(pav==t) // 可利用空间表的头指针指向t(t已不是空闲结点首地址了)
pav=s; // 修改pav,令刚释放的结点为下次分配时的最先查询的结点
}
}
p=NULL; // 令刚释放的结点的指针为空
}