LRU算法研究

来源:互联网 发布:手机录视频软件 编辑:程序博客网 时间:2024/05/27 20:09

LRU是Least Recently Used 近期最少使用算法。

内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
什么是LRU算法? LRU是Least Recently Used的缩写,即最少使用页面置换算法,是为虚拟页式存储管理服务的。
关于操作系统的内存管理,如何节省利用容量不大的内存为最多的进程提供资源,一直是研究的重要方向。而内存的虚拟存储管理,是现在最通用,最成功的方式—— 在内存有限的情况下,扩展一部分外存作为虚拟内存,真正的内存只存储当前运行时所用得到信息。这无疑极大地扩充了内存的功能,极大地提高了计算机的并发度。虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,其余页面放入外存的管理方式。

说明:
对于虚拟页式存储,内外存信息的替换是以页面为单位进行的——当需要一个放在外存的页面时,把它调入内存,同时为了保持原有空间的大小,还要把一个内种调动越少,进程执行的效率也就越高。那么,把哪个页面调出去可以达到调动尽量少的目的?我们需要一个算法。
其实,达到这样一种情形的算法是最理想的了——每次调换出的页面是所有内存页面中最迟将被使用的——这可以最大限度的推迟页面调换,这种算法,被称为理想页面置换算法。可惜的是,这种算法是无法实现的。

为了尽量减少与理想算法的差距,产生了各种精妙的算法,最少使用页面置换算法便是其中一个。LRU算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条指令中频繁使用。反过来说,已经很久没有使用的页面很可能在未来较长的一段时间内不会被用到。这个,就是著名的局部性原理——比内存速度还要快的cache,也是基于同样的原理运行的。因此,我们只需要在每次调换时,找到最少使用的那个页面调出内存。这就是LRU算法的全部内容。

FIFO 、LRU、LFU三种算法

提到缓存,有两点是必须要考虑的:
(1)缓存数据和目标数据的一致性问题。
(2)缓存的过期策略(机制)。
其中,缓存的过期策略涉及淘汰算法。常用的淘汰算法有下面几种:
(1)FIFO:First In First Out,先进先出
(2)LRU:Least Recently Used,最近最少使用
(3)LFU:Least Frequently Used,最不经常使用
注意LRU和LFU的区别。LFU算法是根据在一段时间里数据项被使用的次数选择出最少使用的数据项,即根据使用次数的差异来决定。而LRU是根据使用时间的差异来决定的。
一个优秀的缓存框架必须实现以上的所有缓存机制。例如:Ehcache就实现了上面的所有策略。

双链表+hashtable(有些面试要考)

package com.example.test;import java.util.Hashtable;public class LRUCache{    private int cacheSize;    private Hashtable<Object,Entry> nodes;    private int currentSize;    private Entry first; //链表头    private Entry last; //链表尾    public LRUCache(int i){        currentSize = 0;        cacheSize = i;        nodes = new Hashtable<Object,Entry>(i);//缓存容器    }    /**     *获取缓存中的对象,并把它放在最前面     */    public Entry get(Object key){        Entry node = nodes.get(key);        if(node != null){            moveToHead(node);            return node;        }else{            return null;        }    }    /**     * 添加      */    public void put(Object key,Object value){        //先看看hashtable是否存在该entry,如果存在,则只更新其        Entry node = nodes.get(key);        if(node == null){            if(currentSize >= cacheSize){                nodes.remove(last.key);                removeLast();            }else{                currentSize++;            }            node = new Entry();        }        node.value = value;        //将最新使用的节点放在链表头,表示最新使用        moveToHead(node);        nodes.put(key, node);    }    public void remove(Object key){        Entry node = nodes.get(key);        //在链表中删除        if(node != null){            if(node.prev != null){                node.prev.next = node.next;            }            if(node.next != null){                node.next.prev = node.prev;            }            if(last == node)                last = node.prev;            if(first == node)                first = node.next;        }        //在hashtable中删除        nodes.remove(key);    }    /**     * 删除链表尾部节点,即使用最后 使用的entry     */    private void removeLast(){        //链表尾不为空,则将链表尾指向null,删除连表尾(删除最少引用)        if(last != null){            if(last.prev != null)                last.prev.next = null;            else                first = null;            last = last.prev;        }    }    /**     *移动到链表头,表示最新使用过     */    private void moveToHead(Entry node){        if(node == first)            return;        if(node.prev != null)            node.prev.next = node.next;        if(node.next != null)            node.next.prev = node.prev;        if(last == node)            last =node.prev;        if(first != null){            node.next = first;            first.prev = node;        }        first = node;        node.prev = null;        if(last == null){            last = first;        }    }    /*     * 清空缓存     */    public void clear(){        first = null;        last = null;        currentSize = 0;    }}    class Entry{        Entry prev;        Entry next;        Object value;        Object key;    }
0 0