操作系统:Java实现页面置换算法(OPT,FIFO,LRU)
前言
代码有很多冗余,因为是写作业时写的,不过代码简单易懂,看看就可以改了。
置换算法介绍
页面置换算法(也称为页面淘汰算法)是用来选择换出页面的算法。
在请求页式存储管理方式中,由于一个进程运行的时候不是所有的页面都在内存中,所以会出现缺页中断。
当缺页的时候内存没有空闲的物理块时就需要换出内存中的一页,具体换出哪一页面是由页面置换算法决定的,页面置换算法的优劣直接影响到系统的效率
要注意把页面置换和连续分配方式中的交换区别开来,页面置换的单位是页面而不是整个进程,交换的单位是整个进程
当发生缺页中断后,系统不一定会执行页面置换算法。因为发生缺页中断仅仅说明需要执行的页面没有在内存中,如果内存空间中还有空闲块的话,只需要用缺页中断处理程序把需要的页面从外存调入内存即可。不需要页面置换算法:只有内存中没有空闲块的时候才需要页面置换算法。
所以,缺页中断不一定导致执行页面置换算法。
-
最佳置换算法(OPT)
在预知一个进程的页面号引用串的情况下,每次都淘汰以后不再使用的或以后最迟再被使用的页面,这种算法就是最佳置换算法
显然,最佳置换算法是最优的,具有最低的缺页率。但由于实际操作中往往无法事先知道以后会引用到所有页面的信息,所以最佳置换算法无法实现,只能作为一个标准来衡量其他置换算法的优劣 -
先进先出算法(FIFO)
FIFO算法是最简单的页面置换算法,每次总是淘汰最先进入内存的页面,也就是将在内存存驻留时间最长的页面淘汰掉
该算法实现简单,用一个队列的数据结构就可以实现,将页面按照次序排成一个队列,并设置指针指向最先进入的页面,每次需要淘汰页面时,将指针所指的页面淘汰即可,不过FIFO算法可能会产生Belady一场(缺页次数随着分配的物理块号的增加而增加),而且由于FIFO算法与进程实际运行规律不符,可能会选择淘汰程序经常使用的界面,实际效果不好 -
最近最少使用算法(LRU)
选择最近最久没有被使用的页面予以淘汰,其思想是用以前的页面引用情况来预测将来会出现页面引用情况,也就是假设一个页面刚被访问,那么不久该页面还会被访问。即最佳置换算法是“向后看”,而“LRU”算法则是“向前看”
该算法可以用寄存器组和栈来实现,性能较好
关于算法中的judge
在考虑如何实现判断那一个页面被置换出时,原本是想通过一次次的遍历来得到答案,但是这样代码显得臃肿,于是我添加了一个和frame一样长度的ArrayList:judge,
在 opt 算法中,judge中的数代表页面在以后出现的位置,初始judge给的很大;
在 fifo 算法中,judge中的数代表页面在物理块中存在的时间,初始为0,越大代表存在的时间越长;
在 lru 算法中 judge 中的数代表没被使用的时间,每访问一个页面将访问时间设置为 1,没被访问的其他页面则加1。
如此一来,三种算法都是将judge对应frame中最大的替换出去(就是说三种算法有冗余,还请各位自己修改修改^ - ^。
代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/*
* 中北大学
* 大数据学院
* 数据科学与大数据技术
* 19070542 1907040446
* */
public class Page_replace {
// 最佳置换算法
// opt 最佳页面置换算法
static void opt(ArrayList<Integer> frame, ArrayList<Integer> page) {
System.out.println("============最佳页面置换算法============");
// 框和页面长度
int n_f = frame.size();
int n_p = page.size();
// 缺页
int n_lack = n_f;
// 判断块:初始每个块对应的页面很大
ArrayList<Integer> judge = new ArrayList<Integer>(n_f);
for (int i = 0; i < n_f; i++) {
judge.add(99);
}
for (int i = 0; i < n_p; i++) {
System.out.print(page.get(i) + "===");
if (i < n_f) {
// 预装入
frame.set(i, page.get(i));
System.out.println(frame);
} else {
if (frame.contains(page.get(i))) {
// 页面已经存在在物理快中
System.out.println("页面已经存在于物理块");
} else {
// 更新往后页面第一次出现的位置
for (int j = 0; j < 3; j++) {
int index = 99;
for (int k = i + 1; k < n_p; k++) {
if (frame.get(j) == page.get(k)) {
index = k;
break;
}
}
// 更新(
judge.set(j, index);
}
// 根据出现最后的(即judge对应最大的)替换
int index_max = judge.indexOf(Collections.max(judge));
int rep_page = frame.get(index_max);
frame.set(index_max, page.get(i));
System.out.print(frame);
System.out.println(" 替换掉了页面:" + rep_page);
n_lack = n_lack + 1;
}
}
}
float p_lack = 100 * (float) n_lack / n_p;
System.out.println("===================================");
System.out.printf("缺页次数:%d\n", n_lack);
System.out.printf("缺页率: %.2f%%\n", p_lack);
System.out.println("===================================");
}
// fifo 先行先出算法
// fifo 先进先出置换算法
static void fifo(ArrayList<Integer> frame, ArrayList<Integer> page) {
System.out.println("============先进先出置换算法============");
// 框和页面长度
int n_f = frame.size();
int n_p = page.size();
// 缺页
int n_lack = n_f;
// 判断块:初始每个块对应的出现次数
// 因为在预装入之后才会有相应的判断
// 使用我将判断的状态直接设置成预装入之后 即为 3 2 1
ArrayList<Integer> judge = new ArrayList<Integer>(n_f);
for (int i = 0; i < n_f; i++) {
judge.add(3 - i);
}
for (int i = 0; i < n_p; i++) {
System.out.print(page.get(i) + "===");
if (i < n_f) {
// 预装入
frame.set(i, page.get(i));
System.out.println(frame);
} else {
// 每个页面存在次数加1
for (int j = 0; j < n_f; j++) {
judge.set(j, judge.get(j) + 1);
}
if (frame.contains(page.get(i))) {
// 页面已经存在在物理块中
System.out.println("页面已经存在于物理块");
} else {
// 根据存在最久的(即judge对应最大的)替换
int index_max = judge.indexOf(Collections.max(judge));
int rep_page = frame.get(index_max);
frame.set(index_max, page.get(i));
// 将新换进的存在状态设置为1
judge.set(index_max, 1);
System.out.print(frame);
System.out.println(" 替换掉了页面:" + rep_page);
n_lack = n_lack + 1;
}
}
}
float p_lack = 100 * (float) n_lack / n_p;
System.out.println("===================================");
System.out.printf("缺页次数:%d\n", n_lack);
System.out.printf("缺页率: %.2f%%\n", p_lack);
System.out.println("===================================");
}
// lru 最近最久未使用算法
static void lru(ArrayList<Integer> frame, ArrayList<Integer> page) {
System.out.println("===========最近最久未使用算法===========");
// 框和页面长度
int n_f = frame.size();
int n_p = page.size();
// 缺页
int n_lack = n_f;
// 和fifo类似先设置为 3 2 1
ArrayList<Integer> judge = new ArrayList<Integer>(n_f);
for (int i = 0; i < n_f; i++) {
judge.add(3 - i);
}
for (int i = 0; i < n_p; i++) {
System.out.print(page.get(i) + "===");
if (i < n_f) {
// 预装入
frame.set(i, page.get(i));
System.out.println(frame);
} else {
// 每个页面存在次数加1
for (int j = 0; j < n_f; j++) {
judge.set(j, judge.get(j) + 1);
}
if (frame.contains(page.get(i))) {
// 页面已经存在在物理块中
System.out.println("页面已经存在于物理块");
// 这一步fifo没有
// 将页面的使用重置为1
judge.set(frame.indexOf(page.get(i)), 1);
} else {
// 根据最久未使用的(即judge对应最大的)替换
int index_max = judge.indexOf(Collections.max(judge));
int rep_page = frame.get(index_max);
frame.set(index_max, page.get(i));
// 将新换进的使用状态设置为1
judge.set(index_max, 1);
System.out.print(frame);
System.out.println(" 替换掉了页面:" + rep_page);
n_lack = n_lack + 1;
}
}
}
float p_lack = 100 * (float) n_lack / n_p;
System.out.println("===================================");
System.out.printf("缺页次数:%d\n", n_lack);
System.out.printf("缺页率: %.2f%%\n", p_lack);
System.out.println("===================================");
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入分配给该作业的物理页框块数:");
int n_frame = scanner.nextInt(); // 物理页框数
ArrayList<Integer> frame = new ArrayList<Integer>(n_frame);
for (int i = 0; i < n_frame; i++) {
frame.add(-1);
}
System.out.print("请输入该作业的页面走向:");
scanner.nextLine(); // 控制输入格式
String inputPages = scanner.nextLine();
String[] split = inputPages.split("\\s+|,|,");
int n_page = split.length; // 作业的页面走向总次数
ArrayList<Integer> page = new ArrayList<Integer>(n_page); // 作业的页面走向
for (int i = 0; i < n_page; i++) {
page.add(Integer.parseInt(split[i]));
}
scanner.close();
// 测试输入
// 3
// 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1
opt(frame, page);
fifo(frame, page);
lru(frame, page);
}
}
结果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!