(在學習操作系統時,要做一份有關LRU和clock算法的實驗報告,很多同學都應該是通過數組去實現LRU,可能是對堆棧的使用和鏈表的使用不是很熟悉吧,在網上查資料時看到了LinkedHashMap,於是自己試着用它去實現了LRU.)
LRU算法介紹:
LRU是Least Recently Used 近期最少使用算法。內存管理的一種頁面置換算法,對於在內存中但又不用的數據快(內存塊)叫做LRU,Oracle會根據那些數據屬於LRU而將其移出內存而騰出空間來加載另外的數據,一般用於大數據處理的時候很少使用的數據那么就直接請求數據庫,如果經常請求的數據就直接在緩存里面讀取。
最近最久未使用(LRU)的頁面置換算法,是根據頁面調入內存后的使用情況進行決策的。由於無法預測各頁面將來的使用情況,只能利用“最近的過去”作為“最近的將來”的近似,因此,LRU置換算法是選擇最近最久未使用的頁面予以淘汰。該算法賦予每個頁面一個訪問字段,用來記錄一個頁面自上次被訪問以來所經歷的時間t,當須淘汰一個頁面時,選擇現有頁面中其t值最大的,即最近最久未使用的頁面予以淘汰(可以使用這種方法去實現)。
LRU的實現
可利用一個棧來保存當前使用的各個頁面的頁面號。每當進程訪問某頁面時,便將該頁面的頁面號從棧中移出,將它壓入棧頂。因此,棧頂始終是最新被訪問頁面的編號,而棧底則是最近最久未使用頁面的頁面號。
LRU算法也可以過雙向鏈表來實現,而LinkedHashMap恰好是通過雙向鏈表實現的java集合類,它的一大特點是,以當某個位置被命中,它就會通過調整鏈表的指向,將該位置調整到頭位置,新加入的內容直接放在鏈表頭,如此一來,最近被命中的內容就向鏈表頭移動,需要替換時,鏈表最后的位置就是最近最少使用的位置。
假定現有一進程所訪問的頁面序列為:
4,7,0,7,1,0,1,2,1,2,6
隨着進程的訪問,棧中頁面號的變化情況如圖所示。在訪問頁面6時發生了缺頁,此時頁面4是最近最久未被訪問的頁,應將它置換出去。
代碼如下:
package 作業; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Scanner; import java.util.Set; /*LRU是Least Recently Used 近期最少使用算法。 *通過HashLiekedMap實現LRU的算法的關鍵是,如果map里面的元素個數大於了緩存最大容量,則刪除鏈表頭元素 */ /*public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) *LRU參數參數: *initialCapacity - 初始容量。 *loadFactor - 加載因子(需要是按該因子擴充容量)。 *accessOrder - 排序模式( true) - 對於訪問順序(get一個元素后,這個元素被加到最后,使用了LRU 最近最少被使用的調度算法),對於插入順序,則為 false,可以不斷加入元素。 */ /*相關思路介紹: * 當有一個新的元素加入到鏈表里面時,程序會調用LinkedHahMap類中Entry的addEntry方法, *而該方法又會 會調用removeEldestEntry方法,這里就是實現LRU元素過期機制的地方, * 默認的情況下removeEldestEntry方法只返回false,表示可以一直表鏈表里面增加元素,在這個里 *修改一下就好了。 * */ /* 測試數據: 5 11 4 7 0 7 1 0 1 2 1 2 6 */ import java.util.*; public class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{ private int capacity; //初始內存容量 LRULinkedHashMap(int capacity){ //構造方法,傳入一個參數 super(16,0.75f,true); //調用LinkedHashMap,傳入參數 this.capacity=capacity; //傳遞指定的最大內存容量 } @Override public boolean removeEldestEntry(Map.Entry<K, V> eldest){ //,每加入一個元素,就判斷是size是否超過了已定的容量 System.out.println("此時的size大小="+size()); if((size()>capacity)) { System.out.println("超出已定的內存容量,把鏈表頂端元素移除:"+eldest.getValue()); } return size()>capacity; } public static void main(String[] args) throws Exception{ Scanner cin = new Scanner(System.in); System.out.println("請輸入總共內存頁面數: "); int n = cin.nextInt(); Map<Integer,Integer> map=new LRULinkedHashMap<Integer, Integer>(n); System.out.println("請輸入按順序輸入要訪問內存的總共頁面數: "); int y = cin.nextInt(); System.out.println("請輸入按順序輸入訪問內存的頁面序列: "); for(int i=1;i<=y;i++) { int x = cin.nextInt(); map.put(x, x); } System.out.println("此時內存中包含的頁面數是有:"); //用for-each語句,遍歷此時內存中的頁面並輸出 for(java.util.Map.Entry<Integer, Integer> entry: map.entrySet()){ System.out.println(entry.getValue()); } } }
實驗截圖: