您的位置:首页 > 理论基础 > 数据结构算法

看数据结构写代码(49)边界标识法

2015-04-15 21:44 253 查看
浅谈 内存:内存 两大问题:1. 如何 分配 内存 ? 2. 如何 回收 释放 的 内存

系统 从 可利用 空间表中  寻找 合适 大小的 内存 分配给用户,并 将 释放的 内存 重新 插入 可利用空间表里,以便 下次 继续 分配给用户。可是 怎么 分配 和 回收呢?

内存的分配与回收 有三种策略:1. 首次拟合法 2. 最佳拟合法 3.最差拟合法

1.首次拟合法,

内存分配:从 利用空间表里 从 表头 顺序 查找 第一个 满足大小的 内存,并将其分配给用户,所以其 分配 算法时间 复杂度 为 O(n),n为表长

内存回收:直接 插在表头,时间复杂度 为 O(1)

2.最佳拟合法,

内存分配:从 表里 寻找 一个 大于 等于 并且 最接近 指定 大小的 内存。所以 每次 查找 需要 遍历 表。为了 不用 每次 都遍历 表,将 表 节点 按空间 从小 到大 排列分配时间复杂度 为 O(n),n为表长

内存回收:需要 按 顺序 插入 指定的位置, 时间 复杂度 也是 O(n)

3.最差拟合法:

内存分配:与 表里 寻找 一个 大于等于 并且 是 最大 的 内存。所以 也需要 遍历表。为了不用每次都遍历表,将 表 节点 从 大 到小排列。查找 时间 复杂度为O(1),(最大的总是在第一个)

内存回收:同样 需要 按 顺序 插入 指定位置。时间复杂度O(n)

所以 从效率上来说: 首次 拟合法  > 最差拟合法 > 最佳拟合法

但是 不同 策略 适合 不同的场景:

最佳 拟合法 总是 寻找 最接近的内存,有可能 生成 一些 存储量非常小的内存,造成 无法 分配。但是 同时 也 保留了 很大的内存 以备 以后 分配 大内存之需。适合 内存分配大小 比较 广的情况。

最差拟合法:总是寻找 最大的内存,从而使空闲表的 大小 接近 均匀。适合 分配内存大小 比较窄的情况。

首次拟合法:内存分配是 随机的,因此它 介于 两者之间。

三种策略 都会 造成 一些 内存 非常小的空间,从而 使 这些 内存空间 无法 被分配。为了更有效的利用内存,需要 将 相邻的空闲的内存 合并成一个 更大的内存。

今天的 边界标识法 ,其中的 一种方法。

边界标识法 在 内存 的头部 和尾部 的边界 设置 一个标记, 标记 这个 内存 是空闲 还是 被占用。这使得 在 释放 空间时,易于判断 与 这个 空间 物理位置 相邻的 空间 是否 是空闲。若空闲,则 合并 这些空间

源码网盘地址:点击打开链接

修正回收算法:前继,后继都为空闲时的代码:

else{//前后都可以合并,忘记删除 后继空间了.
pre->nextLink = next->nextLink;
next->nextLink->preLink = pre;//忘记加了..
pre->size += sp->size + next->size;
Space foot = FOOT_LOC(pre);
foot->upLink = pre;
}写这些代码 需要 特别 细心:注意 空表的 情况,注意 指针之间的赋值 的 相互影响,以及 回收时 是否 需要 删除 后继空间。

下面上代码:

// BoundTag.cpp : 定义控制台应用程序的入口点。
//边界标识法 -- 首次拟合法

#include "stdafx.h"
#include <cstdlib>
#define MAX_SIZE 1000
#define ALLOC_MIN_SIZE 10//最小分配空间大小.

typedef struct Word{
union {
Word * preLink;//头部域前驱
Word * upLink;//尾部域,指向结点头部
};
int tag;//0标示空闲,1表示占用
int size;//仅仅表示 可用空间,不包括 头部 和 尾部空间
Word * nextLink;//头部后继指针.
}*Space;
#define FOOT_LOC(p) ((p)+(p->size)-1)//尾部域位置

void initSpace(Space * freeSpace,Space * pav){
//有2个空间是为了 查找空间的邻接点,防止出界用的。
*freeSpace = (Space)malloc((MAX_SIZE+2)*sizeof(Word));
Space head = *freeSpace;
head->tag = 1;//设置边界已占用
head++;//指向第一个节点..
head->tag = 0;//设置节点空闲.
head->preLink = head->nextLink = head;//循环双链表..
head->size = MAX_SIZE;
*pav = head;//设置头指针
Space foot = FOOT_LOC(head);
foot->tag = 0;
foot->upLink = head;
foot++;
foot->tag = 1;//设置 边界 已占用
}
Space userSpace[MAX_SIZE] = {NULL};//用户空间数组.
int usCount = 0;

Space allocBoundTag(Space * pav,int size){
Space p = * pav;
for (;p != NULL && p->size < size && p->nextLink != *pav ; p = p->nextLink) ;
if (p == NULL || p->size < size ){
return NULL;
}
*pav = p->nextLink;//为了 防止 小的空间 都堆积 在 前面...(小空间均匀分布)
if (p->size - size > ALLOC_MIN_SIZE){//从高位截取p,不破坏指针间的关系.
p->size -= size;
Space foot = FOOT_LOC(p);
foot->upLink = p;
foot->tag = 0;
p = foot + 1;
p->size = size;
foot = FOOT_LOC(p);
p->tag = foot->tag = 1;
foot->upLink = p;
}
else{//分配后剩余空间小于 ALLOC_MIN_SIZE
if (p = *pav){//只剩下一个空间了,清空指针
*pav = NULL;
}
else{//直接分配 p->size个空间出去
Space foot = FOOT_LOC(p);
foot->tag =p->tag = 1;
p->preLink->nextLink = p->nextLink;
p->nextLink->preLink = p->preLink;
}
}
userSpace[usCount++] = p;
return p;
}
//回收空间,合并 邻接空闲空间.
void reclaimBoundTag(Space * pav,Space sp){
Space pre = (sp -1)->upLink;//前一个空间
Space next = sp + sp->size;//后一个空间..
int pTag = pre->tag;
int nTag = next->tag;
if (pTag == 1 && nTag == 1){//前后都被占用,直接插入在表头.
Space foot = FOOT_LOC(sp);
foot->tag = sp->tag = 0;
if (pav == NULL){
*pav = sp->preLink = sp->nextLink = sp;
}
else{
sp->nextLink = *pav;
sp->preLink = (*pav)->preLink;
(*pav)->preLink = sp;
//(*pav)->preLink->nextLink = sp;//上一句话 已经改变了 值.
sp->preLink->nextLink = sp;
*pav = sp;//将头指针指向刚释放的空间
}
}
else if(pTag == 0 && nTag == 1){//前面的可以合并..
pre->size += sp->size;
Space foot = FOOT_LOC(pre);
foot->tag = 0;
foot->upLink = pre;
}
else if(pTag == 1 && nTag == 0){//后面的可以合并
sp->preLink = next->preLink;
sp->nextLink = next->nextLink;
next->preLink->nextLink = sp;
next->nextLink->preLink = sp;
sp->size += next->size;
Space foot = FOOT_LOC(sp);
sp->tag = foot->tag = 0;
foot->upLink = sp;
}
else{//前后都可以合并
pre->nextLink = next->nextLink;
pre->size += sp->size + next->size;
Space foot = FOOT_LOC(pre);
foot->upLink = pre;
}
//设置用户空间
for (int i = 0; i < usCount; i++){
if (sp == userSpace[i]){
userSpace[i] = NULL;
}
}
}

void print(Space s){
printf("空间首地址:%0x,空间大小:%d,前驱地址:%0x,后继空间:%0x\n",s,s->size,s->preLink,s->nextLink);
}

void printSpace(Space pav){
if (pav != NULL)
{
printf("--------可利用空间表---------\n");
Space p = pav;
print(p);
for (p = p->nextLink; p != pav; p = p->nextLink){
print(p);
}
}
printf("--------用户空间表---------\n");
for (int i = 0; i < usCount; i++){
Space us = userSpace[i];
if (us){
printf("空间首地址:%0x,空间大小:%d\n",us,us->size);
}
}
}

int _tmain(int argc, _TCHAR* argv[])
{
Space  freeSpace = NULL,pav = NULL;
initSpace(&freeSpace,&pav);
printf("----------初始化-------------\n");
printSpace(pav);
printf("--------------------分配一个500的空间后-------------\n");
Space s500 = allocBoundTag(&pav,500);
printSpace(pav);
printf("--------------------分配一个300的空间后-------------\n");
Space s300 = allocBoundTag(&pav,300);
printSpace(pav);
printf("--------------------分配一个150的空间后-------------\n");
Space s150 = allocBoundTag(&pav,150);
printSpace(pav);
printf("--------------------释放一个300的空间后-------------\n");
reclaimBoundTag(&pav,s300);
printSpace(pav);
printf("--------------------释放一个500的空间后-------------\n");
reclaimBoundTag(&pav,s500);
printSpace(pav);
printf("--------------------分配一个50的空间后-------------\n");
Space s50 = allocBoundTag(&pav,50);
printSpace(pav);
printSpace(pav);
printf("--------------------释放一个50的空间后-------------\n");
reclaimBoundTag(&pav,s50);
printSpace(pav);
printf("--------------------释放一个150的空间后-------------\n");
reclaimBoundTag(&pav,s150);
printSpace(pav);
free(freeSpace);
return 0;
}


其实上面的算法 只是 简单的 模拟了 内存空间的 分配 和释放。

至于 真正的 内存 分配 ,又是怎样呢?等待以后 回答。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  边界标识法