算法与数据结构 algorithms and data structures 第八章 动态存储管理
Post on 19-Dec-2015
311 views
TRANSCRIPT
算法与数据结构Algorithms and Data Structure
s 第八章 动态存储管理
第八章 动态存储管理第八章 动态存储管理8.1 概述 内存是很重要的、很昂贵的资源,如何合理高效地使用这一资源是一个比较复杂的问题。 早期使用低级语言编程,内存管理是由程序员自己负责。程序员负担重,管理水平因人而异,管理效率低,容易出错。 随着操作系统和高级语言的发展,情况不断改善。内存管理分别由操作系统、高级语言的编译系统和程序员分工合作管理。通常编译系统负责静态储存管理,操作系统负责整个内存管理和动态储存管理。
程序员级的管理: 用户程序中所用的储存结构有两种,静态结构 :空间量在编译后,即可确定 动态结构:程序运行中申请空间,编译时无法确定。静态储存由编译系统管理。动态储存由程序员和操作系统管理,但程序员的管理非常简单。程序员的工作就是在需要的时候向系统申请空间,在不需要时释放要来的动态储存空间: C 语言中: malloc(size), 申请 size 字节的内存;
free(p), 释放 p ,归还给系统; C++ 中: new objectType(), 申请空间; free(p), 释放 p ,归还给系统;
Java 语言中: new objectType(), 申请空间;
用户程序:# include iostd.lib……Int main() {… *r=new int[100];…
free (r);…}
操作系统
分配 OS_AllocMemory(r,size,flags)
回收 OS_ReclaimMemory(r)
FreeMem
FreeMem
rFreeMem
编译系统级管理 在编译中,编译系统为程序设置了一个虚拟空间,它管理的是虚拟空间。
用户程序:…int x,y;float r,s;char str[10]; ………
虚拟空间:x: 4bytesy: 4bytes
str: 10bytes
r: 4bytess: 4bytes
0481216
26
内存
程序装入时,重定位
编译原理与技术中将做介绍。
操作系统级管理: 存储管理是操作系统的重要部分之一,操作系统对储存的管理是才是真实的管理,而且这一管理是很复杂的。
操作系统的存储管理
为程序代码和静态数据分配空间
为程序动态分配空间
回收不用的动态空间
回收空间程序代码和静态数据空间
分
配
回
收
执行程序
执行完毕或撤消执行程序
程序
New Otype()
Free(p)
从外部来看,操作系统存储管理系统就是提供存储空间分配和回收服务,但内部实现方法却十分复杂,不同的操作系统采用不同的策略和方法,这些问题将在后续课程操作系统中详细介绍。 这里我们只是站在数据结构的角度来讨论动态存储管理的基本方法,即存储空间的分配和回收基础技术、存储空间的逻辑结构和物理结构。
8.2 可利用空间表 初试状态
OS bootOS 占用空间
free tagsizelink
一个连续的存储空间称为“块” [block]
Tag :标记空间是否分配
Size :空间大小
Link :指向下一个空闲块
初试状态:除了操作系统占用的空间外,其它空间形成一个空闲块。当空闲块多时,用 link 链成一个链表,该链表就是可利用空间表。初试此表中只有一个空闲块。表指针是 free 。
经过多次分配、回收之后,形成了多个空闲块,它们之间不连续,如图所示:
Free
used1
used2
used3
used4
used52 3 4 5 6
Free
1
1
3 6 5 4 2
可利用空间表的链接顺序有: ( 1 )按块的首地址有低到高链接; ( 2 )按块的大小有小到大链接; ( 3 )按块的大小有大到小链接;分配: 一般有 3 种策略,设申请空间的大小为 n
( 1 )首次拟合法:从表头开始搜索,遇到第一个尺寸等于大于 n 的块进行分配;
( 2 )最佳拟合法:搜索整个表,将最小的等于大于 n 的块进行分配;
( 3 )最差拟合法:搜索整个表,将最大块进行分配(等于大于 n );
分配过程:• 找到合适的空闲块 p ;• P.size等于 n 或比 n 大少许(一般设定一个量
s ),则将 p 从表中删除,进行分配;• 若 p.size>n+s ,从 p 的下部切割 size 为 n 的一块进行分配,如图所示: n=16k
0 64kp
1 16k
48k
回收 : 程序释放空间 ( 如 free(p)) 、程序运行结束后将占用的块归还系统,如果收回的块的相邻块是空闲的,需要合并它们。
回收过程:设释放块是 p ,大小为 size 。( 1 ) 设置 p .tag=0 ;( 2 )判断 p 的下相邻块 q 是否空闲
若空闲:从可利用空间表摘出 q ,置 p.size=p.size+q.size( 合并 ) ;
( 3 )判断 p 的上相邻块 r 是否空闲 若空闲:合并 r 和 p , r.size=r.size+p.size
否则:将 p插入可利用空间表。
例如:Free
used1
used2
used3
used4
used52 3 4 5 6
Free
1
13 6 5 4 2
释放 used1
0 4k
1 1k null
0 6k
1
2
used1
0 4k
0 7k null1
2
used1
0 11k1
2
used1
有时也不必马上合并,如果释放块 p 的大小恰好符合下次申请空间的要求,可以将 p 分配,而不必从可利用空间表中切割分配。
Free
13 6 5 4
2
used1
例如,一个使用单链表的程序,它会不断地申请和释放同类型的结点(块大小相等),1 回收时不进行合并,而是放在另一个链表 avail中;2 分配时首先从 avail取一个块分配,当 avail 中没有空闲块时在从 free 表里分配。这样就省去了不断地合并切割的麻烦,可以提高效率。 对于一些小的操作系统,内存管理相对简单些。在许多专用设备、智能仪表和家用电器等都使用一种小型的、高效的、简单的操作系统,一般称之为“嵌入式操作系统”。下面介绍一些实用而简单的动态存储管理系统。
8.3 伙伴系统( Buddy system )特点:( 1 )分配块的大小均是 2k ;( 2 )分配和回收简单 可利用空间表结构:
20
21
22
.
.
.
2k
2m
0 k 0 k 0 k
^^^
内存最大空间是 2m
空闲块按其大小链入各自的链表;该数组是各链表的表头接点
同尺寸的空闲块构成双向循环链表;有 4 个域: tag 标记, 0 空闲, 1 占用, k: 块的大小 2k , llink:q前驱指针, rlingk: 后继指针
伙伴系统抽象数据类型ADT BoddySystem
Data :
int m // 可用内存 2m
FreeHeadList // m 个表头结点构成的线性表
BlockScrpt // 块描述 Memory // 整个内存空间
opration:
BS_malloc(size) // 分配内存 BS_reclaim(BlockScrpt bp) // 回收内存End BlockSystem
Class FreeHeadNode {
int sizePower; // k 2k
BlockScrpt first; // 链表指针} // Class FreeHeadNode
Class FreeHeadList {
int m; //
FreeHeadNode[] list;
public
FreeHeadList(int n) { m=n;
list=new FreeHeadNode[m] ;}
for (k=0; k<=m; k++) { list[k].sizePower=k; first=null;}
} // Class FreeHeadList
k first
0123..
m-1m
^^^
^
^
块描述Class BlockScrpt {
int sizePower;
boolean used;
BlockScrpt llink, rlink;
int add;
public
BlockScrpt(int k, boolean b, int addr) {
sizePower=k; used=b; add=addr;
} // BlockScrpt
}// Class BlockScrpt
伙伴系统结构Class BuddySystem { int m; // 最大可用内存 2m
BlockHeadList headList // 表头向量 BlockScrpt blkScrpt; // 块描述 Byte[] mem; // 内存 public BuddySystem(int k) { // 构造函数 m=k; headList=new BlockHeadList(m); blkScrpt=new BlockScrpt(m,false,0);
blkScrpt.llink=blkscrpt; blkScrpt.rlink=blkscrpt; headList[m].first= blkScrpt mem=new Byte[2m]; } // BlockScrpt BS_malloc(int k) {……} void BS_reclaim(BlockScrpt bp) {……}}// Class BuddySystem
初始状态
012..k..m false m 0
012
2m-1
headList
mem
BlockScrpt
分配 26 之后
..67..k.m-1m
false m-1 2m-1
0
2m-1
2m-1
headList mem
false k 2k
false 7 27
true 6 0
false 6 26
分配算法思想:申请空间量为 2k ;1 从 k 到 m依次搜寻非空链表若无:内存不够,结束;
若有:设为 headList[j] k=<j<=m
2 若 j=k: 从 headList[k] 中取一结点分配,结束;3 若 j>k: 从 headList[j]取一结点 bs
(1) 将 bs均分为二,高地址部分插入 headList[j-1];
j--;
(2) 重复( 1 )直到 j=k ;4 将 bs 分配;结束;
BlockScrpt BS_malloc(int k) { for (j=k; j<=m; j++) // 找非空链表 if (!headList[j].first) break; if (j>m) return null; // 无非空链表,分配失败 bs=headList[j].delet(1); // 从非空链表中取第一个接点; for (s=j; s>k; s--){ // 将大块分割; bst=new BlockScrpt(s-1, false, bs.add+2s-1); headList[s-1].insert(bst); bs.sizePower--; } // for bs.used=true; return bs; // 分配 bs} //
True s-1
False s-1 bst
bs
回收算法思想 伙伴系统的一个重要特点是:任何块(除最大块外)都有唯一的一个伙伴,所谓伙伴即:大小一样且相邻; 空闲的相邻块是可以合并的; 一个块的伙伴地址是什么? 设块的首地址是 p ,其伙伴的首地址是: Buddy(p, k) = p+2k (if p MOD 2k+1=0) = p +2k (if p MOD 2k+1= 2k)
设回收的块是 bs
1 k=bs.sizePower; p=bs.add;2 计算 伙伴地址 q=buddy(p,k);3 从 headList[k]链表中找add等于 q的块描述bst,
若无:伙伴占用,将 bs 插入 headList[k],结束; 否则:将 bs和 bst合并,用 bs 描述。 K++;4 重复 3 直到 k>m;5 将 bs 插入 headList[m]。
8.4 一个小型的动态存储管理系统
算法 6.8:中序遍历线索二叉树
算法 6.9: 求p的后继( p在前序序列中的后继 )
算法 6.10: 前序遍历线索二叉树
6.4 6.4 森林与二叉树森林与二叉树
6.5 6.5 归并查找集(归并查找集( Merge Find Set MFSMerge Find Set MFS ))
改进后算法:
Huffman算法:
算法 6.11 Huffman编码
void initiate(char c[], float w[]) {
void selectMin(int k, int &s, int &t){
void getCode(){