使用java.util.LinkedList模擬實現內存頁面置換算法--LRU算法


一,LRU算法介紹

LRU是內存分配中“離散分配方式”之分頁存儲管理方式中用到的一個算法。每個進程都有自己的頁表,進程只將自己的一部分頁面加載到內存的物理塊中,當進程在運行過程中,發現某頁面不在物理內存塊中(發生缺頁異常)就需要從磁盤把相應的頁面調入內存。而若內存已經滿了的情況下,需要將內存中暫時不用的物理塊頁面 換出到磁盤(交換空間)中,那到底換出哪一頁呢?LRU算法就是用來解決到底換出哪一頁 的這個問題。

LRU算法是最近最少未使用算法。當內存缺頁時,總是優先選出距離當前最久未使用的頁面換出,並把當前的缺頁換入。該算法可用棧模擬實現。

棧頂總是保存當前最近訪問的頁面號,棧底則總是保存最久未訪問的頁面號。對於下一個頁面,有兩種情況:

①命中,則需要:更新棧頂元素。即將當前命中的頁面號放到棧頂。

②未命中,這里還需要考慮棧是否滿了。1)若棧未滿,直接將未命中的頁面號放到棧頂即可。為什么要放到棧頂(LinkedList表頭)呢?

因為,LRU每次總是選取最近最久未被訪問的頁面淘汰。某頁面剛剛被訪問,需要放到棧頂,以表示它不是“最近最久 未訪問的頁面”

2)棧已經滿了,則需要選中一頁換出(棧底元素是最久未訪問的頁面),然后再將新頁面放入棧頂

 

二,代碼實現

import java.util.LinkedList;

public class LRU {

    private LinkedList<Integer> stack;//模擬頁面'寄存器'
    private int size;//寄存器大小,表示一共可裝入多少頁面
    
    public LRU(int size) {
        stack = new LinkedList<>();
        this.size = size;
    }

    //LRU算法簡單實現,返回一共未命中的次數
    public int lru(int[] pageNumbers)
    {
        if(size <= 0 || pageNumbers == null)
            throw new IllegalArgumentException("illegal arugments");
        
        if(pageNumbers.length <= size)
            return pageNumbers.length;
        
        int unhit = 0;
            
        for(int i = 0; i < pageNumbers.length; i++)
        {
            int index = isHit(pageNumbers[i]);
            if(index == -1)
                unhit = processUnHit(pageNumbers[i], unhit);
            else
            {
                processHit(pageNumbers[i], index);
            }
        }
        return unhit;
    }
    
    /**
     * 
     * @param pageNumber 判斷 pageNumber是否hit
     * @return -1 表示 unhit, 其他表示hit
     */
    private int isHit(int pageNumber){
        return stack.indexOf(pageNumber);
    }
    
    /**
     * 當棧未滿時,未命中的頁面號直接入棧;棧滿時,需要替換頁面,先選中一個頁面(棧底)刪除,然后Push新頁面
     * @param pageNumber 未命中的頁面號
     * @param count 當前未命中次數
     * @return 更新后的未命中的次數
     */
    private int processUnHit(int pageNumber, int count){
        if(isFull())
            stack.removeLast();//刪除最久未訪問的頁面
        stack.push(pageNumber);//放入最近訪問的頁面
        count++;//未命中的次數加1
        return count;
    }
    
    //處理命中的情況
    private void processHit(int pageNumber, int index){
            stack.push(stack.remove(index));
    }
    
    //判斷'寄存器'棧是否已經滿了
    private boolean isFull()
    {
        if(stack.size() < size)
            return false;
        else
            return true;
    }
    
    //test
    public static void main(String[] args) {
        int[] pageNumbers = {4,7,1,1,7,2,1};
        int size = 2;
        LRU lru = new LRU(size);
        System.out.println(lru.lru(pageNumbers));
    }
}

 

三,復雜度分析

由於java.util.LinkedList 實現了棧的功能。push()方法總是將元素放到表頭,pop()方法總是從鏈表的表頭刪除元素。在這里,鏈表的表頭代表棧頂。

因此,當某頁面號命中時,需要從鏈表中找到該頁面的位置(index),然后刪除該頁面,並將它push到鏈表的表頭。---processHit()方法

由於是鏈表,故尋找某頁面的時間復雜度為O(N),最壞情況下掃描整個鏈表。

當頁面未命中的,需要將頁面號push到鏈表表頭。push之前,先檢查棧是否已經滿了。若未滿,直接push入棧,時間復雜度為O(1);如果棧已經滿了,需要刪除鏈表的表尾元素(相當於棧底元素--removeLast()時間復雜度也為O(1),因為LinkedList本質上是一個雙向鏈表。)然后,再將該刪除的元素push到棧頂--時間復雜度為O(1)

故未命中時,處理的總的時間復雜度還是O(1)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM