看数据结构写代码(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;
}写这些代码 需要 特别 细心:注意 空表的 情况,注意 指针之间的赋值 的 相互影响,以及 回收时 是否 需要 删除 后继空间。
下面上代码:
其实上面的算法 只是 简单的 模拟了 内存空间的 分配 和释放。
至于 真正的 内存 分配 ,又是怎样呢?等待以后 回答。
系统 从 可利用 空间表中 寻找 合适 大小的 内存 分配给用户,并 将 释放的 内存 重新 插入 可利用空间表里,以便 下次 继续 分配给用户。可是 怎么 分配 和 回收呢?
内存的分配与回收 有三种策略: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; }
其实上面的算法 只是 简单的 模拟了 内存空间的 分配 和释放。
至于 真正的 内存 分配 ,又是怎样呢?等待以后 回答。
相关文章推荐
- Informix数据表结构分析资料整理之约束查询代码
- 数据结构与算法笔记 —— 查找算法及代码实现
- 看数据结构写代码(41) 强连通分量
- 数据结构——停车场代码
- 看数据结构写代码(5)静态链表
- 【数据结构与算法】【排序】冒泡排序的代码实现
- java数据结构之线性表代码实现
- C#数据结构之循环链表的实例代码
- 数据结构——BinarySearch两种方法的代码实现
- C语言 数据结构之链表实现代码
- 数据结构课程设计【附上代码】
- Hadoop_NameNode_代码分析_数据结构(1)
- 数据结构课程设计代码--电子投票系统
- 看数据结构写代码(42)最小生成树
- 看数据结构写代码(20)稀疏矩阵(顺序存储方式)
- 看数据结构写代码(30) 树的双亲孩子表示法的实现
- 数据结构中,二叉树用到的算法代码综合
- 有一个1,2,3....49 的数组,通过控制台显示为7行7列的,并且里面的数据时随机的代码
- 看数据结构写代码(31)树的二叉链表的实现
- 【数据结构与算法】【查找】插值查找的代码实现