问题重述:
固定和可变分区内存管理方法都存在缺陷。固定分区由于分区数目是固定的,因此限 制了活动进程的个数,而且当可用内存大小与进程内存需求大小不匹配时,内存使用效率非 常低效。而可变分区方法管理起来较为复杂,而且由于进程镜像的上下浮动会带来额外开销。 伙伴系统是二者的一种折中,兼具固定和可变分区的优点。
在伙伴系统中,内存块个数和大小是不固定的,但是大小只能限定为 2K 个字节,其中 L ≤ K≤ U
l 2L = 最小块的大小(字节)
l 2U = 最大块的大小(字节)。通常是整个内存的大小。 开始时,整个可供内存分配的空间被看做一个块,其大小为 2U。如果请求的内存大小为 s, 且 2U-1 < s ≤ 2U,那么把整个内存,即 2U 分配给它。否则将整个内存空间分为两个大小为 2U-1 的伙伴块,并将其中的一个暂时分配给该请求。然后再考查,该 2U-1 大小的块中,条件
2U-2
(1) 存在 L< i ≤ U,使得 2i-1
(2) s≤ 2L 时,把大小为 2L 的块分配给该请求,算法退出。
添加了一个新的静态成员变量,赋初值为1,用于表示当前分区节点的编号
static int number =1;
public int ID; //分区编号
public int status =0; //分区状态,1 表示已分配,0 表示空闲
public int size =0; //分区大小,只能为2L ~ 2U
public Chunk leftBuddy = null ; //左伙伴
public Chunk rightBuddy = null ;//右伙伴
static int number =1;
public Chunk();
类的构造方法,每次在生成新的节点时调用构造方法给当前的节点编号,实现每个节点都有唯一的一个编号
public Chunk(){
this.ID=this.number;
this.number++;
}
public void printMemMap();
遍历二叉树的所有节点,将二叉树的分区号、分区状态、分区大小输出到控制台中。
基本思想:采用先序遍历的方法将每一个节点的信息打印出来,对于当前节点的叶子节点不为空则打印叶子节点,以此类推
public void printMemMap() {
System.out.println("分区号:"+this.ID);
System.out.println("分区状态:"+this.status);
System.out.println("分区大小:"+this.size);
System.out.println();
if(this.leftBuddy != null) {
this.leftBuddy.printMemMap();
this.rightBuddy.printMemMap();
}
}
public int Release(int id);
释放指定节点所占用的内存,采用遍历的方法找到需要释放内存的节点,当释放该节点后判断该节点的兄弟节点是否也为空,是则合并这两个节点,否则不能合并
注意:因为在合并时需要删除当前节点和其兄弟节点,所以应当用其父节点来访问当前节点,否则不能够删除当前节点和其兄弟节点
//释放内存并判断合并节点
public int Release(int id) {
// 释放内存
if(this.leftBuddy.ID == id)
this.leftBuddy.status=0;
if(this.rightBuddy.ID == id)
this.rightBuddy.status=0;
// 合并
if (this.leftBuddy.status==0&&this.rightBuddy.status==0) {
this.status=0;
this.leftBuddy = null;
this.rightBuddy = null;
System.out.println("合并节点成功");
return 0;
}
// 不能合并
else {
System.out.println("不能合并节点");
return -1;
}
}
public int Allocate(int size);
根据所需要的内存大小动态分配内存
基本思想:
首先判断需要分配的内存大小是否在该节点大小的一半到最大值之间,如果是则分配该节点,否则将该节点分成大小小相等的两个节点,再用子节点来分配,一直重复之前的操作,知道子节点的内存大小为1(k)时。
注意:再分配过程中需要考虑到优先分配左节点还是右节点,只有当做节点都不能满足分配条件时才将右节点分解,再进行分配
public int Allocate(int size) {
if(this.status == 0){//父节点未被占用
if(size>this.size/2 && size<=this.size){//大于空间大小的一半,小于该空间大小
this.status=1;
return this.ID;
}
else if(size=1){//比空间大小一半要小,空间折半后至少为1k
int flag;
this.status=1;
this.leftBuddy=new Chunk();
this.rightBuddy=new Chunk();
this.leftBuddy.size=this.size/2;
this.rightBuddy.size=this.size/2;
flag=this.leftBuddy.Allocate(size);
return flag;
}
else
return -1;
}
else if(this.leftBuddy !=null && this.rightBuddy!=null){//父节点被占用,但子节点有未被占用的
int flagl,flagr;
flagl=this.leftBuddy.Allocate(size);
if(flagl>0)
return flagl;
else{
flagr =this.rightBuddy.Allocate(size);
if(flagr >0)
return flagr;
else
return -1;
}
}
else//父节点被占用,且子节点也被占用
return -1;
}
因为算法不够优化,还存在一些问题,所以得到的结果和手工计算的还有差别,当在进行小量分配时不会出现错误(和手工计算一样),当二叉树的深度较大时会出现一些误差(和手工分配不一样)