操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程

前言:虽说操作系统的课程已经结束,但是我觉得梁老师的课上有很多值得记录和回味的东西,时隔多日,我决定还是来写一些博客,记录我这门课上写的实践作业。

题目:用C语言(也可以用Java)实现采用首次适应算法的内存分配和回收过程。

题目要求:

  1. 定义管理空闲分区的相关数据结构:采用空闲分区链表来管理系统中所有的空闲分区,链表中的每个节点表示一个空闲分区,登记有空闲分区的起始地址和长度。
  2. 定义一个简单的进程控制块,其中有对应进程分配到的内存的起始地址和长度、以及进程的状态信息。当提交一个作业申请内存的要求时,便为该作业创建一个进程(此时,需要分配给它一个进程控制块以及它需要的内存空间)。简单起见,进程控制块用一个数组来实现,分配给作业n的进程控制块为其中第n个元素;而状态信息中执行和就绪表示为一种状态,忽略阻塞状态。
  3. 实现首次适应算法的内存分配函数alloc_mem(int len),其中的参数为所申请的内存空间的长度,函数返回值为所分配到的内存空间的起始地址,分配时优先将空闲区的低端部分分配出去,如果空闲区较大,则留下的高端部分仍为空闲区;实现回收内存的函数free_mem(int base, int len),其中的参数为回收区的起始地址和长度,回收时要求进行空闲分区的合并。
  4. 在main()中通过一些具体的分配和回收动作来测试上述内存分配和回收的函数,每完成一个动作,要求将进程的详细信息和所有空闲分区的详细信息显示出来。

分析:首先,我们需要三个类,分别为内存、小内存块(分区)和PCB(用来记录进程信息),至于适应算法的实现,我们可以在内存类的内部实现,也可以用外部方法实现。

如果大家对四个适应算法不熟悉的话可以看看书或者查查资料,在这里我就不进行过多的描述。

PS.这里我就写出我的核心代码,部分例如toString()方法的代码省略

(1).三个数据结构

1.小内存块Hole(可以表示内存中的各个分区,也可以表示进程对应的内存块)

public class Hole {

    private int head;       //小内存块的起始地址
    private int size;       //小内存块的大小
    private boolean isFree; //小内存块的空闲状态

    public Hole(int head, int size) {
        this.head = head;
        this.size = size;
        this.isFree = true;
    }

}

2.PCB(记录进程信息和进程对应的内存块)

public class Pcb {

    private int id;     //进程id
    private int state;  //进程状态 0为空闲 1为就绪 2为执行 3为阻塞
    private Hole hole;  //进程所对应的小内存块

    public Pcb(int id, int state, Hole hole) {
        this.id = id;
        this.state = state;
        this.hole = hole;
    }

}

3.内存

public class Memory {
    private int size;              //内存块大小
    private int lastFind;          //上次寻址结束的下标
    private LinkedList pcbs;  //记录内存块中进程的双向链表
    private LinkedList holes;       //记录内存块分区的双向链表
    private static final int MIN_SIZE = 5 //最小剩余分区大小
}

(2).Memory类的内部方法

1.构造函数

public Memory(int size) {
    this.size = size;                //初始化内存大小
    this.pcbs = new LinkedList<>();  //初始化两个链表
    this.holes = new LinkedList<>();
    holes.add(new Hole(0, size));    //分区链表的首项为大小是内存大小的空闲分区
}

2.申请内存

由于我是用外部方法实现对分区的计算,所以在内部类中只是单纯的分配Hole。

public Memory getMemory(int size, int location, Hole hole) {    //size为申请大小 location为分配分区的下标 hole为location对应的分区
    if (hole.getSize() - size >= MIN_SIZE) {                    //若分配后当前分区剩余大小大于最小分区大小,则把当前分区分为两块
        Hole newHole = new Hole(hole.getHead() + size, hole.getSize() - size);
        holes.add(location + 1, newHole);
        hole.setSize(size);
    }
    pcbs.add(new Pcb(10000 + (int)(89999 * Math.random()), 1, hole));   //模拟添加一个就绪状态的进程,此进程id随机生成(忽略id重复的情况哈)
    hole.setFree(false);    //设置当前分区为非空闲状态
    System.out.println("成功分配大小为" + size + "的内存");
    return this;
}

3.释放内存(通过分区id或者模拟的进程id释放,此方法在类Memory中)

释放时有四种情况,分别是释放分区前的分区为空闲分区、释放分区后的分区为空闲分区、释放分区前后的分区都为空闲分区和释放分区前后的分区都不为空闲分区,在代码中我都会解释。

public Memory releaseMemory(int id) {
    Pcb pcb = null;     //记录此id对应进程(忽略进程id与分区id相同,但进程不同的情况哈)
    if (id >= holes.size()) {   //若id大于holes的表长度,则需要判断此id是否是进程id
        boolean flag = false;
        for (int i = 0; i < pcbs.size(); i++) { //循环比对此id是否是pcbs链表中进程的id
            if (pcbs.get(i).getId() == id) {
                pcb = pcbs.get(i);
                flag = true;
                break;
            }
        }
        if (!flag) {
            System.err.println("无此分区:" + id);
            return this;
        }
    }
    if (pcb != null) {  //若是通过进程id释放内存,则用下列循环获取进程对应的hole对应holes链表的下标(获取分区id)
        for (int i = 0; i < holes.size(); i++) {
            Hole hole = holes.get(i);
            if ((pcb.getHole().getSize() == hole.getSize()) && (pcb.getHole().getHead() == hole.getHead())) {
                id = i;
                break;
            }
        }
    }
    Hole hole = holes.get(id);  //此id为分区id
    if (hole.isFree()) {
        System.out.println("此分区空闲,无需释放:\t" + id);
        return this;
    }
    //用分区id释放pcb
    for (int i = 0; i < pcbs.size(); i++) {
        Pcb pcb2 = pcbs.get(i);
        if ((pcb2.getHole().getSize() == hole.getSize()) && (pcb2.getHole().getHead() == hole.getHead())) {
            pcbs.remove(i);
            break;
        }
    }
    //如果回收分区不是尾分区且后一个分区为空闲, 则与后一个分区合并
    if (id < holes.size() - 1 && holes.get(id + 1).isFree()) {
        Hole nextHole = holes.get(id + 1);
        hole.setSize(hole.getSize() + nextHole.getSize());
        holes.remove(nextHole);
    }
    //如果回收分区不是首分区且前一个分区为空闲, 则与前一个分区合并
    if (id > 0 && holes.get(id - 1).isFree()) {
        Hole lastHole = holes.get(id - 1);
        lastHole.setSize(hole.getSize() + lastHole.getSize());
        holes.remove(id);
        id--;
    }
    holes.get(id).setFree(true);
    System.out.println("内存回收成功!");
    return this;
}

这里用两个判断就完成了对四种情况的处理,大家仔细看看哈。

(3).四个适应算法

1.首次适应算法

public Memory 首次适应算法(Memory memory, int size) {
    int sum = 0;
	//循环内存中所有分区
    for (int i = 0; i < memory.getHoles().size(); i++) {
        sum++;
	//为循环首次适应算法设置最后寻址的下标
        memory.setLastFind(i);
        Hole hole = memory.getHoles().get(i);   //获得对应的分区
	//若此分区空闲且大小大于申请的大小,则申请内存
        if (hole.isFree() && hole.getSize() >= size) {  
            System.out.println("查找" + sum + "次");
            return memory.getMemory(size, i, hole);
        }
    }
    System.err.println("OUT OF MEMORY!");
    return memory;
}

2.循环首次适应算法

public Memory 循环首次适应算法(Memory memory, int size) {
    Hole hole = memory.getHoles().get(memory.getLastFind());
    //判断最后寻址的分区的大小是否足够
    if (hole.isFree() && hole.getSize() >= size) {
        return memory.getMemory(size, memory.getLastFind(), hole);
    }
    int length = memory.getHoles().size();
    int sum = 0;    //为区分与首次适应算法循环次数所设置
    //如果不够,则从下一个分区开始循环
    for (int i = (memory.getLastFind() + 1) % length; i != memory.getLastFind(); i = (i + 1) % length) {
        sum++;
        memory.setLastFind(i);
        hole = memory.getHoles().get(i);
        if (hole.isFree() && hole.getSize() >= size) {
            System.out.println("查找" + sum + "次");
            return memory.getMemory(size, i, hole);
        }
    }
    System.err.println("OUT OF MEMORY!");
    return memory;
}

3.最佳适应算法

public Memory 最佳适应算法(Memory memory, int size)  {
    int findIndex = -1;         //最佳分区的下标
    int min = memory.getSize(); //min存储当前找到的最小的合适的分区大小
    for (int i = 0; i < memory.getHoles().size(); i++) {
        //memory.setLastFind(i);
        Hole hole = memory.getHoles().get(i);
        if (hole.isFree() && hole.getSize() >= size) {
	//若当前找到的分区大小比min还要合适(剩余空间更小),则修改其值
            if (min > hole.getSize() - size){   
                min = hole.getSize() - size;
                findIndex = i;
            }
        }
    }
    if (findIndex != -1) {  //若存在合适分区
        return memory.getMemory(size, findIndex, memory.getHoles().get(findIndex));
    }
    System.err.println("OUT OF MEMORY!");
    return memory;
}

4.最坏适应算法

public Memory 最坏适应算法(Memory memory, int size) {
    int findIndex = -1;
    int max = 0;
    for (int i = 0; i < memory.getHoles().size(); i++) {
        Hole hole = memory.getHoles().get(i);
        if (hole.isFree() && hole.getSize() >= size) {
            if (max < hole.getSize() - size){
                max = hole.getSize() - size;
                findIndex = i;
            }
        }
    }
    if (findIndex != -1) {
        return memory.getMemory(size, findIndex, memory.getHoles().get(findIndex));
    }
    System.err.println("OUT OF MEMORY!");
    return memory;
}

(4).运行结果

         1.初始化大小为500的内存并初始化大小为100 50 40 200 100的进程

         操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第1张图片

         2.申请两个最小内存(5)释放内存(2号分区)

         操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第2张图片

         3.释放相邻尾内存

         操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第3张图片        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第4张图片

         4.释放相邻首内存

         操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第5张图片        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第6张图片

        5.最佳适应算法与最坏适应算法(进程大小为5)

        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第7张图片        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第8张图片

        6.首次适应算法与循环首次适应算法(进程大小为50)

        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第9张图片        操作系统——动态分区管理: 用Java实现适应算法的内存分配和回收过程_第10张图片

项目源代码已上传至GitHub:https://github.com/xhy1999/operation-system

你可能感兴趣的:(操作系统,操作系统,动态分区管理,适应算法,内存分配,JAVA)