实验三 模拟存储器分配算法 (java)
1. 实验目的
了解动态分区分配方式中使用的数据结构和分配算法,并进一步加深对动态分区存储管理方式及其实现过程的理解。
2.实验内容
用高级语言分别实现采用首次适应算法和最佳适应算法的动态分区分配过程alloc()和回收过程free()。其中,空闲分区通过空闲分区链来管理;在进行内存分配时,系统优先使用空闲区低端的空间。
假设初始状态下,可用的内存空间为640KB,并有下列的请求序列:
作业1申请130KB。作业2申请60KB。
作业3申请100KB。作业2释放60KB。
作业4申请200KB。作业3释放100KB。
作业1释放130KB。作业5申请140KB。
作业6申请60KB。作业7申请50KB。作业6释放60KB。
请分别采用首次适应算法和最佳适应算法进行内存块的分配和回收,要求每次分配和回收后显示出空闲内存分区链的情况。
3.运用知识
动态分区分配算法
其中内容有,内存回收,首次适应算法,最佳适应算法。
首次适应算法 来自百度百科
从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给作业,这种方法目的在于减少查找时间。
首次适应算法的特点(First Fit):
该算法倾向于优先利用内存中低址部分的空闲分区,从而保留了高址部分的大空闲区,这为以后到达的大作业分配大的内存空间创造了条件。
缺点
低址部分不断被划分,会留下许多难以利用的,很小的空闲分区,称为碎片。而每次查找又都是从低址部分开始的,这无疑又会增加查找可用空闲分区时的开销。
最佳适应算法 来自百度百科
最佳适应算法是指从全部空闲区中找出能满足作业要求且大小最小的空闲分区的一种计算方法,这种方法能使碎片尽量小。
最佳适应算法(Best Fit):
它从全部空闲区中找出能满足作业要求的、且大小最小的空闲分区,这种方法能使碎片尽量小。为适应此算法,空闲分区表(空闲区链)中的空闲分区要按从小到大进行排序,自表头开始查找到第一个满足要求的自由分区分配。该算法保留大的空闲区,但造成许多小的空闲区。
4.代码设计
package com.atM.taskSystemTest.task3;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
class MeSpaceArea { //内存空间区域类,代表内存空间的一个个分区
private int MeSpace;//该区域的内存空间大小(如果有作业,代表作业占的空间大小)
private boolean empty;//该区域是否为空(是否有作业)//true代表空
private int task_id; //作业号
private int space_id; //区域号 本次实验默认从0开始
@Override
public String toString() {
return "| " + space_id + " | " + MeSpace + " | " + (empty ? "空闲" : task_id + "号作业占用") + " |";
}
public MeSpaceArea(int meSpace, boolean empty, int space_id) {
MeSpace = meSpace;
this.empty = empty;
this.space_id = space_id;
}
public int getSpace_id() {
return space_id;
}
public void setSpace_id(int space_id) {
this.space_id = space_id;
}
public int getTask_id() {
return task_id;
}
public void setTask_id(int task_id) {
this.task_id = task_id;
}
public int getMeSpace() {
return MeSpace;
}
public void setMeSpace(int meSpace) {
MeSpace = meSpace;
}
public boolean isEmpty() {
return empty;
}
public void setEmpty(boolean empty) {
this.empty = empty;
}
}
/**
* @author M.博
* @version 1.0.0
* @ClassName VirtuallyDB.java
* @Description 模拟存储器分配算法
*/
public class VirtuallyDB {
static LinkedList<MeSpaceArea> list; //代表内存,存放一个个分区。
//初始化
static void Int() {
list = new LinkedList<>();
//在这里设置内存空间总大小
list.add(new MeSpaceArea(640, true, 0));
}
/**
* @description: 动态分配
* @param: method //如果为1代表首次适应算法 如果为2代表最佳适应算法
* @return: void
* @author M.博
*/
static void alloc(int method) {
System.out.println("请输入作业号:");
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println("请输入内存大小:");
int size = scanner.nextInt();
if (method == 1) {
if (FF(n, size)) {
System.out.println("内存分配成功,分配方式:首次适应");
} else {
System.out.println("内存分配失败,内存可以空间不足");
}
} else {
if (BF(n, size)) {
System.out.println("内存分配成功,分配方法:最佳适应");
} else {
System.out.println("内存分配失败,内存可以空间不足");
}
}
}
/**
* @description: 更新内存区域的id
* 由于根据两个算法的描述,可以知道每次只需要将内存区域的id重新排列即可
* 不会出现id缺失或者id乱序的情况
* @author M.博
*/
static void reSpaceID() {
for (int i = 0; i < list.size(); i++) {
list.get(i).setSpace_id(i);
}
}
/**
* @description: 首次适应算法(向内存空间中做添加操作)
* @param: n作业号 size内存大小
* @return: boolean //判断是否添加成功
* @author M.博
*/
static boolean FF(int n, int size) {
for (int i = 0; i < list.size(); i++) {
MeSpaceArea e = list.get(i);
if (e.isEmpty() && e.getMeSpace() >= size) {
e.setTask_id(n);
int meSpace = e.getMeSpace();
e.setMeSpace(size);
e.setEmpty(false);
size = meSpace - size;//这时的size是该区域剩余空间大小
if (size > 0)
list.add(i + 1, new MeSpaceArea(size, true, e.getSpace_id() + 1));
reSpaceID();
return true;
}
}
return false;
}
/**
* @description: 最佳适应算法 (向内存空间中做添加操作)
* @param: n作业号 size内存大小
* @return: boolean //判断是否添加成功
* @author M.博
*/
static boolean BF(int n, int size) {
int l = list.size();
//tempList存放内存空间中区域状态为空的区域
LinkedList<MeSpaceArea> tempList = new LinkedList<>();
for (int i = 0; i < l; i++) {
if (list.get(i).isEmpty()) {
tempList.add(list.get(i));
}
}
//将临时空闲链表排序(Lambda表达式)
tempList.sort(((o1, o2) -> o1.getMeSpace() - o2.getMeSpace()));
for (int i = 0; i < tempList.size(); i++) {
MeSpaceArea e = tempList.get(i);
if (e.getMeSpace() >= size) {
e.setTask_id(n);
int meSpace = e.getMeSpace();
e.setMeSpace(size);
e.setEmpty(false);
list.set(e.getSpace_id(), e);//这里用的是更改 方法,找到e在list表中的位置,替换e。e是temp排序表中找到的合适选项
size = meSpace - size;//这时的size是该区域剩余空间大小
if (size > 0)
list.add(e.getSpace_id() + 1, new MeSpaceArea(size, true, e.getSpace_id() + 1));
reSpaceID();
return true;
}
}
return false;
}
/**
* @description: 返回周围空间情况,1代表前一个为空,2代表后一个为空,3代表两个都空,0代表都不空
* @return: int
* @author M.博
*/
static int aroundSpaceState(int id) {
int ans = 0;
//判断前一个
if (id != 0 && list.get(id - 1).isEmpty()) {
ans += 1;
}
if (id != list.size() && list.get(id + 1).isEmpty()) {
ans += 2;
}
return ans;
}
/**
* @description: 回收函数
* 根据区域周围情况,进行区域作业回收
* @author M.博
*/
static void free() {
System.out.println("请输入要回收的作业ID");//注意这里的id是作业号
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
//循环从0开始,刚好是第一个地址号
for (int i = 0; i < list.size(); i++) {
MeSpaceArea e = list.get(i);
if (!e.isEmpty() && e.getTask_id() == n) {
int state = aroundSpaceState(i);
switch (state) {
case 1:
list.get(i - 1).setMeSpace(list.get(i - 1).getMeSpace() + list.get(i).getMeSpace());
list.remove(i);
reSpaceID();
break;
case 2:
list.get(i).setMeSpace(list.get(i + 1).getMeSpace() + list.get(i).getMeSpace());
list.get(i).setEmpty(true);
list.remove(i + 1);
reSpaceID();
break;
case 3:
list.get(i - 1).setMeSpace(list.get(i - 1).getMeSpace() + list.get(i).getMeSpace() + list.get(i + 1).getMeSpace());
list.remove(i);
list.remove(i);//注意这里一定不要写i+1,上面remove i 后 i的下一个就变成了i
reSpaceID();
break;
case 0:
list.get(i).setEmpty(true);
break;
default:
break;
}
System.out.println("内存回收成功");
break;
}
}
}
//输出表格 实验需要
static void toPrintState() {
System.out.println("内存分区情况表:");
System.out.println("|分区号|分区大小(KB)| 状态 |");
AtomicInteger i = new AtomicInteger();
list.forEach((e) -> {
System.out.print(e.toString());
System.out.println(" test:" + i.getAndIncrement());
});
}
//测试初始化函数
// static void test(){
// FF(1,5);
// FF(2,70);
// FF(3,80);
// FF(4,60);
// FF(5,40);
// FF(6,30);
// FF(7,50);
// }
public static void main(String[] args) {
System.out.println("分区模拟:");
Int();
// test();测试用
//这里我用的是标志法退出外层的while循环,也可以使用别的方法,例如将while里边的true改2为一个boolean型变量,在5的情况下改变其状态。
args:
while (true) {
System.out.println("请输入要进行的操作:");
System.out.println("1-首次适应算法,2-最佳适应算法,3-内存回收,4-显示内存状况,5退出");
Scanner scanner = new Scanner(System.in);
switch (scanner.nextInt()) {
case 1:
alloc(1);
break;
case 2:
alloc(2);
break;
case 3:
free();
break;
case 4:
toPrintState();
break;
case 5:
break args;
default:
break;
}
}
}
}
5.运行结果演示
首次适应算法模拟情况
这里不发实验结果了,比较简单,大家可以尝试一下
最佳适应算法模拟情况
6.实验可能遇到的问题及解释
aroundSpaceState(int id)
该函数的作用是,返回周围空间情况,1代表前一个为空,2代表后一个为空,3代表两个都空,0代表都不空
这是为了回收过程中对应区域周围情况不同需要做出不同的操作做准备
free()
该函数是回收函数,会根据aroundSpaceState(int id)函数给出的情况做出操作,一定要注意,在list去掉应该元素后,list内部的index也会改变.
ps
本次实验数据较简单,本人测试过两个算法都用,再回收,都比较顺利,有兴趣的朋友可以试试其他数据及其他方式。
如果哪里有问题,也请赐教