内存页面置换算法 JAVA实现 附源码 概述 内存页面置换算法是操作系统中用于管理虚拟内存的重要组成部分,它负责在内存不足时选择合适的页面进行替换,以便为新的页面腾出空间。以下是几种常见的内存页面置换算法:
最佳(Optimal)置换算法: 这是一种理想化的算法,它在选择要被置换的页面时选择未来最长时间内不会被访问的页面。尽管这个算法可以达到最佳的性能,但是由于需要预测未来页面访问情况,实际中很难实现。
先进先出(FIFO)置换算法: 这是一种简单的置换算法,它选择最早进入内存的页面进行替换。虽然实现简单,但是它存在”Belady异常”,即增加内存大小时可能会导致缺页次数反而增加的情况。
最近最少使用(LRU)置换算法: 这个算法选择最近最长时间未被访问的页面进行替换。LRU算法通过维护一个页面访问历史记录或者使用特定的数据结构(如链表或者有序数组)来实现。LRU算法在实践中性能较好,但是实现起来可能会比较复杂。
最近最不经常使用(LFU)置换算法: 这个算法选择最近访问次数最少的页面进行替换。LFU算法适用于一些特定的工作负载,但是可能受到访问次数和时间窗口的影响。
时钟(Clock)置换算法: 这是一种简化的近似LRU算法,它维护一个环形缓冲区,并使用一个指针按顺序查看页面的访问情况。当需要替换页面时,指针停在最近未被访问的页面上,然后替换它。这个算法相对较简单且性能较好。
实验说明:真实的内存页面访问应该符合局部性原理,本次实验的页面访问队列为随机生成,所以部分算法命中率可能会和预期不符
页面置换流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 graph TB Z[返回α页面,结束] B(接收页面访问请求) B --> E{访问内存中页号为α的页面} E -->|内存未命中| X[从磁盘中加载α页面] E -->|内存命中| Z X --> Y{内存页是否已满} Y --> |否|P[α页面放入内存] P --> Z Y --> |是|T[进行页面替换] T --> G[选择替换算法] G -->|CLOCK| H[CLOCK算法] G -->|LRU| J[LRU算法] G -->|LFU| K[LFU算法] G -->|FIFO| L[FIFO算法] H --> M[更新内存页面] J --> M K --> M L --> M M --> Z
算法实现 主要有VirtualMemoryManagement
、PageFrame
两个类,VirtualMemoryManagement
用来管理内存块,里面有各种页面置换算法,PageFrame
中保存着内存块的各种信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class PageFrame { private int pageNumber = -1 ; private int accessTime = 0 ; private int recentAccess = 0 ; private int futureAccess = 0 ; private int accessStatus = 0 ; public void addAccessTime () }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public class VirtualMemoryManagement { public static int memoryPageSize = 3 ; public static int accessNum = 100 ; public static int pageNumber = 10 ; public static int time = 0 ; public static int clockPointer = 0 ; public static int missingPageNum = 0 ; public static int permutationNum = 0 ; public List<PageFrame> pageFrameList = new ArrayList <>(memoryPageSize); public List<Integer> accessList ; public static List<Integer> initAccessList () public static List<Integer> getAccessList (List<Integer> accessList) public void printMemory () public boolean memContains (Integer accessNumber) public void run (String type) private void addAccessRecentTime () private void replacePageFrame (Integer accessNumber,String type) private void CLOCK (PageFrame pageFrame) private void OPT (PageFrame pageFrame) private void LRU (PageFrame pageFrame) private void LFU (PageFrame pageFrame) private void FIFO (PageFrame pageFrame) private void addPageFrame (Integer accessNumber) public static void printResult () public static void resetResult () }
FIFO 先进先出,这个算法实现起来非常简单就像我们排队一样,值得一提的是FIFO方法会产生belady
异常,这个belady
异常说的就是正常情况下增加内存页数量应该会增加内存的命中率,但是当使用FIFO算法时,把内存页面数从3加到4,不能保证增加内存命中率。
1 2 3 4 private void FIFO (PageFrame pageFrame) { pageFrameList.remove(0 ); pageFrameList.add(pageFrame); }
LRU LFU 这个算法非常像,LFU是替换最近用的次数最少的页面,LRU是替换最久不用的页面。
这两个算法基于局部性原理,最近经常使用的页面,下次使用的可能性更大;使用越多的页面,下次使用的可能性更大;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private void LRU (PageFrame pageFrame) { PageFrame recentPageFrame= pageFrameList.get(0 ); for (int i = 1 ; i < pageFrameList.size(); i++) { PageFrame oldPageFrame = pageFrameList.get(i); if (recentPageFrame.getRecentAccess()<oldPageFrame.getRecentAccess()){ recentPageFrame = oldPageFrame; } } pageFrameList.remove(recentPageFrame); pageFrameList.add(pageFrame); } private void LFU (PageFrame pageFrame) { PageFrame leastPageFrame= pageFrameList.get(0 ); for (int i = 1 ; i < pageFrameList.size(); i++) { PageFrame oldPageFrame = pageFrameList.get(i); if (leastPageFrame.getAccessTime()>oldPageFrame.getAccessTime()){ leastPageFrame = oldPageFrame; } } pageFrameList.remove(leastPageFrame); pageFrameList.add(pageFrame); }
OPT OPT(Optimal)页面置换算法是一种理论上的最佳页面置换算法,也称为最佳置换算法。它是一种基于未来访问页面的策略,即根据未来最长时间内不会被访问的页面进行置换。
在OPT页面置换算法中,系统会预测未来每个页面的访问情况,并选择未来最长时间内不会被访问的页面进行置换。这种算法的优点是可以最大程度地减少页面置换的次数,从而提高系统的性能。
实际上很难准确预测未来页面的访问情况,因此OPT算法通常只用作理论研究,而在实际应用中很少被使用。
这里我遍历访问队列,如果找到和内存中一样的页面,就表示在未来我需要访问这个页面,就设置pageFrame的未来访问位=1,找到(内存数-1)个未来访问页面时,此时在内存中未来访问位为0的页面就是我要替换的,因为它是未来最长时间内不会被访问的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void OPT (PageFrame pageFrame) { int memorySize = pageFrameList.size(); for (Integer integer : accessList) { for (PageFrame frame : pageFrameList) { if (frame.getPageNumber() == integer){ frame.setFutureAccess(1 ); memorySize--; if (memorySize==1 ){ for (PageFrame frame2 : pageFrameList) { if (frame2.getFutureAccess()==0 ){ pageFrameList.remove(frame2); pageFrameList.add(pageFrame); return ; } } } } } } }
CLOCK CLOCK(Clock Replacement)页面置换算法是一种基于近似LRU(Least Recently Used)算法的页面置换算法。它使用一个类似时钟的数据结构来维护页面的访问情况,并根据页面的访问位和修改位来进行页面置换的决策。
CLOCK算法维护一个环形缓冲区,其中每个页面都有一个访问位(用于表示页面是否被访问过)和一个修改位(用于表示页面是否被修改过)。当需要置换页面时,算法会在环形缓冲区中顺时针地查找页面,如果找到一个未被访问过的页面(访问位为0),则选择该页面进行置换。如果所有页面的访问位都为1,则会继续顺时针地查找,直到找到一个未被访问过的页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 private void CLOCK (PageFrame pageFrame) { while (true ){ PageFrame replacePageFrame = pageFrameList.get(clockPointer); if (replacePageFrame.getAccessStatus() == 1 ){ replacePageFrame.setAccessStatus(0 ); }else { pageFrameList.remove(replacePageFrame); pageFrameList.add(pageFrame); break ; } clockPointer = ++clockPointer%pageFrameList.size(); } }
源码 PageFrame.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class PageFrame { private int pageNumber = -1 ; private int accessTime = 0 ; private int recentAccess = 0 ; private int futureAccess = 0 ; public int getFutureAccess () { return futureAccess; } public void setFutureAccess (int futureAccess) { this .futureAccess = futureAccess; } private int accessStatus = 0 ; public int getAccessStatus () { return accessStatus; } public void setAccessStatus (int accessStatus) { this .accessStatus = accessStatus; } public int getPageNumber () { return pageNumber; } public void setPageNumber (int pageNumber) { this .pageNumber = pageNumber; } public int getAccessTime () { return accessTime; } public void setAccessTime (int accessTime) { this .accessTime = accessTime; } public void addAccessTime () { this .accessTime++; } public void addRecentAccessTime () { this .recentAccess++; } public int getRecentAccess () { return recentAccess; } public void setRecentAccess (int recentAccess) { this .recentAccess = recentAccess; } }
**VirtualMemoryManagement.class
**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 public class VirtualMemoryManagement { public static int memoryPageSize = 3 ; public static int accessNum = 10 ; public static int pageNumber = 10 ; public static int time = 0 ; public static int clockPointer = 0 ; public static int missingPageNum = 0 ; public static int permutationNum = 0 ; public List<PageFrame> pageFrameList = new ArrayList <>(memoryPageSize); public List<Integer> accessList ; public List<PageFrame> getPageFrameList () { return pageFrameList; } public void setPageFrameList (List<PageFrame> pageFrameList) { this .pageFrameList = pageFrameList; } public List<Integer> getAccessList () { return accessList; } public void setAccessList (List<Integer> accessList) { this .accessList = accessList; } public static List<Integer> initAccessList () { List<Integer> accessList =new ArrayList <>(accessNum); System.out.print("访问内存页序列:" ); for (int i = 0 ; i < accessNum; i++) { int value = (int )(Math.random() * pageNumber)+1 ; accessList.add(value); System.out.print(" " +value); } System.out.println(); return accessList; } public static List<Integer> beladyList () { Integer[] belady = {1 ,2 ,3 ,4 ,1 ,2 ,5 ,1 ,2 ,6 ,7 ,5 }; return Arrays.asList(belady); } public static List<Integer> getAccessList (List<Integer> accessList) { accessList = new ArrayList <>(accessList); return accessList; } public void printMemory () { System.out.println("--------------------内存-------------------" ); if (pageFrameList.isEmpty()){ System.out.print("内存当前为空" ); } for (PageFrame pageFrame : pageFrameList) { System.out.print("No " +pageFrame.getPageNumber()); System.out.print(" | " ); } System.out.println(); } public boolean memContains (Integer accessNumber) { for (PageFrame pageFrame : pageFrameList) { if (pageFrame.getPageNumber() == (accessNumber)){ pageFrame.addAccessTime(); pageFrame.setAccessStatus(1 ); pageFrame.setRecentAccess(0 ); return true ; } } return false ; } public void run (String type) { System.out.println("--------------------" +type+"算法-------------------" ); while (!accessList.isEmpty()){ printMemory(); Integer accessNumber = accessList.get(0 ); if (memContains(accessNumber)){ }else { if (pageFrameList.size()<memoryPageSize){ addPageFrame(accessNumber); }else if (pageFrameList.size()==memoryPageSize){ replacePageFrame(accessNumber,type); } } accessList.remove(0 ); addAccessRecentTime(); time++; } printMemory(); printResult(); resetResult(); } private void addAccessRecentTime () { for (PageFrame pageFrame : pageFrameList) { pageFrame.addRecentAccessTime(); } } private void replacePageFrame (Integer accessNumber,String type) { permutationNum++; PageFrame pageFrame = new PageFrame (); pageFrame.setPageNumber(accessNumber); switch (type){ case "FIFO" : FIFO(pageFrame); break ; case "LRU" : LRU(pageFrame); break ; case "LFU" : LFU(pageFrame); break ; case "OPT" : OPT(pageFrame); break ; case "CLOCK" : CLOCK(pageFrame); break ; } } private void CLOCK (PageFrame pageFrame) { while (true ){ PageFrame replacePageFrame = pageFrameList.get(clockPointer); if (replacePageFrame.getAccessStatus() == 1 ){ replacePageFrame.setAccessStatus(0 ); }else { pageFrameList.remove(replacePageFrame); pageFrameList.add(pageFrame); break ; } clockPointer = ++clockPointer%pageFrameList.size(); } } private void OPT (PageFrame pageFrame) { int memorySize = pageFrameList.size(); for (Integer integer : accessList) { for (PageFrame frame : pageFrameList) { if (frame.getPageNumber() == integer){ frame.setFutureAccess(1 ); memorySize--; if (memorySize==1 ){ for (PageFrame frame2 : pageFrameList) { if (frame2.getFutureAccess()==0 ){ pageFrameList.remove(frame2); pageFrameList.add(pageFrame); return ; } } } } } } } private void LRU (PageFrame pageFrame) { PageFrame recentPageFrame= pageFrameList.get(0 ); for (int i = 1 ; i < pageFrameList.size(); i++) { PageFrame oldPageFrame = pageFrameList.get(i); if (recentPageFrame.getRecentAccess()<oldPageFrame.getRecentAccess()){ recentPageFrame = oldPageFrame; } } pageFrameList.remove(recentPageFrame); pageFrameList.add(pageFrame); } private void LFU (PageFrame pageFrame) { PageFrame leastPageFrame= pageFrameList.get(0 ); for (int i = 1 ; i < pageFrameList.size(); i++) { PageFrame oldPageFrame = pageFrameList.get(i); if (leastPageFrame.getAccessTime()>oldPageFrame.getAccessTime()){ leastPageFrame = oldPageFrame; } } pageFrameList.remove(leastPageFrame); pageFrameList.add(pageFrame); } private void FIFO (PageFrame pageFrame) { pageFrameList.remove(0 ); pageFrameList.add(pageFrame); } private void addPageFrame (Integer accessNumber) { missingPageNum++; PageFrame pageFrame = new PageFrame (); pageFrame.setPageNumber(accessNumber); pageFrameList.add(pageFrame); } public static void printResult () { System.out.println(); System.out.println("缺页次数: " +missingPageNum); System.out.println("置换次数: " +permutationNum); System.out.println("缺页率: " +(double )missingPageNum/(double ) accessNum); System.out.println("命中率: " + (1 - (((double )missingPageNum + (double ) permutationNum)/(double ) accessNum))); } public static void resetResult () { missingPageNum = 0 ; permutationNum = 0 ; } public static void main (String[] args) { VirtualMemoryManagement FIFO = new VirtualMemoryManagement (); VirtualMemoryManagement FIFO2 = new VirtualMemoryManagement (); VirtualMemoryManagement LRU = new VirtualMemoryManagement (); VirtualMemoryManagement LFU = new VirtualMemoryManagement (); VirtualMemoryManagement CLOCK = new VirtualMemoryManagement (); List<Integer> integers = initAccessList(); List<Integer> beladyList = beladyList(); FIFO.setAccessList(getAccessList(integers)); FIFO2.setAccessList(getAccessList(beladyList)); LRU.setAccessList(getAccessList(integers)); LFU.setAccessList(getAccessList(integers)); CLOCK.setAccessList(getAccessList(integers)); CLOCK.run("CLOCK" ); } }