简要介绍及前提假设
学习操作系统存储管理这一章节后,我们都很清楚可变分区是内存管理中一种比较好的策略,是后续虚拟存储技术的基础。所以做这个模拟用可变分区的方式管理内存的实验。
内存管理方法介绍
本实验模拟的是用可变分区的方式管理内存的,使用两张表来管理内存,一张表是已经分配的内存块表,另一个张表是未分配的内存块表,这两张表构成了伙伴系统(因为他们都是针对内存的,针对同一个东西操控)。
三种内存分配算法介绍
- (1)首次适应算法 各个空闲地址块按照首地址从小到大的顺序排列,找到第一个能够满足某程序要求空间大小的空闲块分配给该程序。
- (2)最佳适应算法 各个空闲块按照空间地址范围大小从小到大排列,找到第一个能够满足某程序要求空间大小的空闲块分配给该程序。
- (3)最坏适应算法 各个空闲块按照空间地址范围大小从大到小排列,找到第一个能够满足某程序要求空间大小的空闲块分配给该程序。
内存块回收算法介绍
当某程序的内存空间被释放,需要把相应的内存空间回收,该内存块由已分配状态变为未分配状态。这里需要注意的是,该内存块和原有的空闲内存块有上邻、下邻、上下邻、上下都不邻的四种情况,如有相邻,要合并。
笔者给出假设
(1)假定可以申请的内存地址空间范围是0-6000。
(2)假定初始化内存快照
算法设计
有上面的介绍后,我们很清楚地知道按照可变分区的方式管理内存的特点及分配内存、回收内存的算法思路。由此,很容易设计算法如下。
开发环境及工程文件架构
Windows10+Java8+记事本+eclipse单步调试!
源代码
MemoryManagement.java
import java.util.*;
public class MemoryManagement{
public ArrayList<MemoryBlock> usedMemoryBlockList;//已分配内存块表
public ArrayList<MemoryBlock> unusedMemoryBlockList;//未分配内存块表
public int threshold;//门限值
public int memoryAllocationAlgorithm;//内存分配算法。1--首次适应,2--最佳适应,3--最坏适应
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
/*
*完成初始化
*/
MemoryManagement mm = new MemoryManagement();
mm.init();
mm.menu();
System.out.print("请选择内存分配算法: ");
mm.memoryAllocationAlgorithm = scan.nextInt();
System.out.print("请输入门限值: ");
mm.threshold = scan.nextInt();
/*
*开始实验
*/
String allocateOrRecover;//分配或回收
String programName;//程序名
int programSize;//程序大小
while(true){
System.out.println("===============================================================");
System.out.println("\n输入格式:[allocate 程序名 程序大小]或[recover 程序名]");
allocateOrRecover = scan.next();
if(allocateOrRecover.equals("allocate")){
programName = scan.next();
programSize = scan.nextInt();
mm.runAllocate(programName, programSize);
}else if(allocateOrRecover.equals("recover")){
programName = scan.next();
mm.recover(programName);
}else{
System.out.println("输入有误,请按照[allocate 程序名 程序大小]或[recover 程序名]格式重新输入");
}
System.out.println("===============================================================");
}
}
//初始化,并打印给出某中间状况的内存快照
public void init(){
usedMemoryBlockList = new ArrayList<MemoryBlock>();
unusedMemoryBlockList = new ArrayList<MemoryBlock>();
usedMemoryBlockList.add(new MemoryBlock("programA",0,500));
usedMemoryBlockList.add(new MemoryBlock("programB",5500,500));
usedMemoryBlockList.add(new MemoryBlock("programC",2500,1500));
usedMemoryBlockList.add(new MemoryBlock("programD",500,800));
unusedMemoryBlockList.add(new MemoryBlock("unused",1300,1200));
unusedMemoryBlockList.add(new MemoryBlock("unused",4000,1500));
print();
}
//打印内存快照
public void print(){
//为了打印出图形化界面,这里开一个临时的ArrayList
ArrayList<MemoryBlock> aList = new ArrayList<MemoryBlock>();
//先按照内存块首址排序,之后再打印
unusedMemoryBlockList.sort(new MyComparatorFirstAdaptation());
usedMemoryBlockList.sort(new MyComparatorFirstAdaptation());
System.out.println("-----------------------------------");
System.out.println("已分配内存块表");
System.out.println("起始地址\t大小\t程序名");
for(int i = 0; i < usedMemoryBlockList.size(); i++){
System.out.println(usedMemoryBlockList.get(i).getStartAddress()+"\t\t"+usedMemoryBlockList.get(i).getSize()+"\t"+usedMemoryBlockList.get(i).getState());
aList.add(usedMemoryBlockList.get(i));
}
System.out.println();
System.out.println("-----------------------------------");
System.out.println("未分配内存块表");
System.out.println("起始地址\t大小\t状态");
for(int i = 0; i < unusedMemoryBlockList.size(); i++){
System.out.println(unusedMemoryBlockList.get(i).getStartAddress()+"\t\t"+unusedMemoryBlockList.get(i).getSize()+"\t"+unusedMemoryBlockList.get(i).getState());
aList.add(unusedMemoryBlockList.get(i));
}
System.out.println("-----------------------------------");
System.out.println();
System.out.println("图形化如下");
aList.sort(new MyComparatorFirstAdaptation());
for(int i = 0;i < aList.size();i++) {
System.out.printf("%10d ------------------\n",aList.get(i).getStartAddress());
boolean first = true;
for(int j=0;j<(aList.get(i).getSize()+1000)/1000;j++) {
if(first){
System.out.println(" | |"+aList.get(i).getState());
first = false;
}else{
System.out.println(" | |");
}
}
if(aList.get(i).getStartAddress()+aList.get(i).getSize() == 6000){
System.out.printf("%10d ------------------\n",aList.get(i).getStartAddress()+aList.get(i).getSize());
}
}
}
//内存分配
public void runAllocate(String programName, int programSize){
boolean flag = false;//如果没有找到满足的内存块
switch(memoryAllocationAlgorithm){
case 1:
unusedMemoryBlockList.sort(new MyComparatorFirstAdaptation());
break;
case 2:
unusedMemoryBlockList.sort(new MyComparatorBestAdaptation());
break;
case 3:
unusedMemoryBlockList.sort(new MyComparatorWorstAdaptation());
break;
}
for(int i = 0 ; i < unusedMemoryBlockList.size(); i++){
MemoryBlock mbTmp = unusedMemoryBlockList.get(i);
if(mbTmp.getSize() >= programSize){
flag = true;
if(mbTmp.getSize() - programSize <= threshold){
usedMemoryBlockList.add(new MemoryBlock(programName, mbTmp.getStartAddress(), mbTmp.getSize()));
}else{
usedMemoryBlockList.add(new MemoryBlock(programName, mbTmp.getStartAddress(), programSize));
unusedMemoryBlockList.add(new MemoryBlock("unused",mbTmp.getStartAddress()+programSize,mbTmp.getSize() - programSize));
}
unusedMemoryBlockList.remove(i);
break;
}
}
if(flag == false){
System.out.println("没有空闲块可以满足。。");
}
print();
}
//回收
public void recover(String programName){
usedMemoryBlockList.sort(new MyComparatorFirstAdaptation());
unusedMemoryBlockList.sort(new MyComparatorFirstAdaptation());
boolean flag = false;
int loc=0;
for(int i = 0; i < usedMemoryBlockList.size(); i++){
if(usedMemoryBlockList.get(i).getState().equals(programName)){
flag = true;
loc = i;
break;
}
}
//如果没找到这个程序,就报告没有该程序,无法回收
if(flag == false){
System.out.println("找不到该程序,无法回收该程序。。");
}else{
MemoryBlock mbTmp = usedMemoryBlockList.get(loc);
//先判断上邻、下邻、上下邻、上下都不邻
boolean up = false;
boolean down = false;
int upLoc=0, downLoc=0;
for(int j = 0; j < unusedMemoryBlockList.size(); j++){
if(mbTmp.getStartAddress() == unusedMemoryBlockList.get(j).getStartAddress()+unusedMemoryBlockList.get(j).getSize()){
upLoc = j;
up = true;
}
if(mbTmp.getStartAddress() + mbTmp.getSize()== unusedMemoryBlockList.get(j).getStartAddress()){
downLoc = j;
down = true;
}
}
if(up==true && down ==true){
MemoryBlock mb = new MemoryBlock("unused",unusedMemoryBlockList.get(upLoc).getStartAddress(),mbTmp.getSize()+unusedMemoryBlockList.get(upLoc).getSize()+unusedMemoryBlockList.get(downLoc).getSize());
usedMemoryBlockList.remove(loc);
unusedMemoryBlockList.remove(upLoc);//删除了upLoc,导致数组个数少1
unusedMemoryBlockList.remove(downLoc-1);//由于数组个数少1,这里注意是downLoc-1
unusedMemoryBlockList.add(mb);
}else if(up == true && down == false){
unusedMemoryBlockList.add(new MemoryBlock("unused",unusedMemoryBlockList.get(upLoc).getStartAddress(),mbTmp.getSize()+unusedMemoryBlockList.get(upLoc).getSize()));
usedMemoryBlockList.remove(loc);
unusedMemoryBlockList.remove(upLoc);
}else if(up == false && down == true){
unusedMemoryBlockList.add(new MemoryBlock("unused",mbTmp.getStartAddress(),mbTmp.getSize()+unusedMemoryBlockList.get(downLoc).getSize()));
usedMemoryBlockList.remove(loc);
unusedMemoryBlockList.remove(downLoc);
}else{
unusedMemoryBlockList.add(new MemoryBlock("unused",mbTmp.getStartAddress(),mbTmp.getSize()));
usedMemoryBlockList.remove(loc);
}
}
print();
}
//用户选择菜单
public void menu(){
System.out.println("*******************menu*******************");
System.out.println("1-----------------首次适应");
System.out.println("2-----------------最佳适应");
System.out.println("3-----------------最坏适应");
System.out.println();
}
//自定义排序比较器
class MyComparatorFirstAdaptation implements Comparator<MemoryBlock>{
public int compare(MemoryBlock mb1, MemoryBlock mb2){
return mb1.getStartAddress() - mb2.getStartAddress();
}
}
class MyComparatorBestAdaptation implements Comparator<MemoryBlock>{
public int compare(MemoryBlock mb1, MemoryBlock mb2){
return mb1.getSize() - mb2.getSize();
}
}
class MyComparatorWorstAdaptation implements Comparator<MemoryBlock>{
public int compare(MemoryBlock mb1, MemoryBlock mb2){
return mb2.getSize() - mb1.getSize();
}
}
}
MemoryBlock.java
public class MemoryBlock{
private int startAddress;//内存块起始地址
private int size;//内存块大小
private String state;//内存块状态,要么是未分配,要么是程序名
public MemoryBlock(){}
public MemoryBlock(String programName, int startAddress, int size){
this.state = programName;
this.startAddress = startAddress;
this.size = size;
}
public void setStartAddress(int startAddress){
this.startAddress = startAddress;
}
public int getStartAddress(){
return this.startAddress;
}
public void setSize(int size){
this.size = size;
}
public int getSize(){
return this.size;
}
public void setState(String programName){
this.state = programName;
}
public String getState(){
return this.state;
}
public String toString(){
return ("[state: "+state+", startAddress: "+startAddress+", size: "+size+"]");
}
//测试
public static void main(String[] args){
MemoryBlock mb = new MemoryBlock();
mb.setStartAddress(1000);
mb.setSize(200);
mb.setState("p1");
System.out.println(mb.getStartAddress());
System.out.println(mb.getSize());
System.out.println(mb.getState());
System.out.println(mb.toString());
System.out.println(new MemoryBlock("programName", 2000, 1000));
}
}
实验结果
(1)最佳适应算法及门界限值实验结果
(2)最坏适应算法实验结果
(3)首次适应算法及上下均无相邻回收内存实验结果
(4)上邻、下邻、上下邻回收内存实验结果
反思总结
笔者刚开始开发的时候用的是记事本,一直有个bug,不知怎么回事!回收programC程序老是错误。为之奈何?为之奈何?此刻想起了eclipse,利用单步调试,利用单步调试,很容易会发现问题,找到了问题,原来是增加或删除ArrayList的元素后,导致元素总数增加/减少,再去删除原来搜索到的那个ArrayList的第i个元素,可能已经变了。。。
不要老是用记事本了,记得还是用调试工具来的块些啊。。。