LRU最近最久未使用的淘汰,这是现阶段应用最广泛的cache命中算法。现阶段应用的LRU算法一般是基于双链表实现的,这个算法的好处是,在查找cache和查找淘汰页面的复杂度都是O(1),只要cache空间足够大,这个命中率是相当不错的。原理如下:
整个cache是放在一个HashMap中,页面有key和value值,同时将Cache的所有位置都用双连表连接起来,当一个页面被命中之后,就将通过调整链表的指向,将该位置调整到链表头的位置,如果是新加入的Cache那么就直接加到链表头中。
如此循环,在多次查找命中和淘汰后,最近被命中的,就会被向链表头方向移动,而没有命中的,而想链表后面移动,链表尾则表示最近最久未使用的Cache。当需要替换页面的时候,链表的最后位置就是最应该被淘汰的位置,算法就只需要淘汰链表最后页面。
下面是tomcat所使用的一个LRU算法:
public class LRUCache {
/**
* 链表节点
* @author Administrator
*
*/
class CacheNode {
CacheNode prev;//前一节点
CacheNode next;//后一节点
Object value;//值
Object key;//键
CacheNode() {
}
}
public LRUCache(int i) {
currentSize = 0;
cacheSize = i;
nodes = new Hashtable(i);//缓存容器
}
/**
* 获取缓存中对象
* @param key
* @return
*/
public Object get(Object key) {
CacheNode node = (CacheNode) nodes.get(key);
if (node != null) {
moveToHead(node);
return node.value;
} else {
return null;
}
}
/**
* 添加缓存
* @param key
* @param value
*/
public void put(Object key, Object value) {
CacheNode node = (CacheNode) nodes.get(key);
if (node == null) {
//缓存容器是否已经超过大小.
if (currentSize >= cacheSize) {
if (last != null)//将最少使用的删除
nodes.remove(last.key);
removeLast();
} else {
currentSize++;
}
node = new CacheNode();
}
node.value = value;
node.key = key;
//将最新使用的节点放到链表头,表示最新使用的.
moveToHead(node);
nodes.put(key, node);
}
/**
* 将缓存删除
* @param key
* @return
*/
public Object remove(Object key) {
CacheNode node = (CacheNode) 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;
}
return node;
}
public void clear() {
first = null;
last = null;
}
/**
* 删除链表尾部节点
* 表示 删除最少使用的缓存对象
*/
private void removeLast() {
//链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
if (last != null) {
if (last.prev != null)
last.prev.next = null;
else
first = null;
last = last.prev;
}
}
/**
* 移动到链表头,表示这个节点是最新使用过的
* @param node
*/
private void moveToHead(CacheNode 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;
}
private int cacheSize;
private Hashtable nodes;//缓存容器
private int currentSize;
private CacheNode first;//链表头
private CacheNode last;//链表尾
}
上几周,我导师和我谈了下关于LRU算法的改进,由上我们可以知道,LRU是没有计数功能的,也就是说如果我一个页面是处于一种周期性访问的性质,而每次这个页面到来访问cache的时候它已经被淘汰了,但是其实它是属于周期性经常访问的页面内容,下面举个实例:
比如我设cache的大小是8,而来的页面内容是1,2,1,4,5,5,7,8,9,10,11,1,12,13,5,14,15,16,17,20,1,3,18,5
如果是上面这种情况,你会发现cache命中次数为2。很悲剧的一种情况,但是很可能常见,好了下面我们来探讨下我改进这个LRU算法的思想:
既然LRU没有计数功能,那么我们就给它一个计数参数,但是我们要保证查找cache和查找淘汰页面的复杂度都是小于O(n)的,不然代价会太高了。好我先在建立一个HashMap的时候除了key和value,再增加一个count。初始的时候cache中没有页面,如果是新加入的Cache那么就直接加到链表头中。同时count=0+200,当一个页面被命中之后,还是和上面规则一样将通过调整链表的指向,将该位置调整到链表头的位置,同时count=count+200。
如此循环,但是替换规则次数要调整了,(不然要count做什么呢 哈哈)。替换规则是这样的,我们设定一个K=3的参数,每次没有命中的页面需要替换页面的时候,我们就去比较链表最后的k个页面count值最小的页面,然后把新的页面加在链表的最前面,把最后面k个页面中count值最小的页面淘汰了。
基于这个规则我们在来看上面那个例子,你会发现cache命中次数为6了。当然只是一个特例还没有大规模测试,同时你会提出一个问题,很有可能有情况就是开始一个页面的访问次数特别多,然后count值很高如果这样是不是这个页面就没办法淘汰了,也许后来它根本就不被访问的。恩!我也这么想的,所以继续改进,在有count后我们还要设定一个规则,就是衰减规则。我们不能让count一成不变,根据我们cache访问的特点。我们可以在设定一个p值,这个p值是每次访问的衰减率,也就是每次访问我们自动对cache里面页面所有的count值减去一p的衰减值,这样如果它之后久久不被访问,那么它也会马上被淘汰。
(如有建议请跟帖,如有错误请指正,第一次发博客@.@)
分享到:
相关推荐
实验的主要目标包括理解和实现 LRU 算法的调度过程,并计算出访问页面时的命中率。 实验内容分为以下部分: 1. 设计一个虚拟存储系统,其中包含一个可以容纳一定数量页面的驻留集(内存)。在实验中,驻留集的大小...
- **实验结果**:通过实验数据可以发现,在大多数情况下,LRU算法的命中率明显高于FIFO算法。这是因为LRU算法更好地利用了局部性原理,避免了FIFO算法中可能出现的Belady现象。 - **测试用例**:通过对不同规模和...
LRU(Least Recently Used,最近最少使用)是一种常用的页面替换算法,用于解决计算机内存不足时如何选择淘汰数据的问题。在缓存或者数据库管理系统中,LRU算法被广泛应用,因为它的实现简单且效果良好。 LRU算法的...
LRU(Least Recently Used,最近最久未使用)算法是一种常用的页面置换算法,它基于一个假设:最近被访问过的页面在未来最有可能再次被访问。在操作系统中,LRU算法用于决定何时将页面从内存移出,以便为新的页面...
在实际应用中,LRU算法能够有效提高缓存命中率,降低系统响应时间,从而提升整体性能。然而,值得注意的是,LRU算法并非适用于所有场景,例如在高度随机的访问模式下,可能会出现频繁替换的问题。因此,在选择缓存...
用C语言实现的OPT和LRU算法,下载后直接用VC++6.0打开即可编译运行。亲测可用。
5. **统计信息**:可能包括替换次数、命中率、缺页率等关键性能指标,帮助用户评估不同参数设置下的LRU算法表现。 通过这个模拟器,学习者不仅可以深入了解LRU算法的原理,还可以观察到不同输入条件下算法的响应,...
3. 分析与讨论:如何分析LRU算法的性能,如缓存命中率等指标。 4. 扩展思考:如何优化LRU算法,如使用双向链表和自平衡二叉搜索树等。 通过对这些文件的深入学习和实践,学生可以全面了解LRU算法的运作机制,并提升...
LRU页面淘汰算法模拟程序(源码)C++ 产生一个进程的大小,构建页表并对页表进行初始化,随后生成访问的指令地址流(是一系列需要访问的指令的地址)。不失一般性,可以适当地(人工指定或随机数产生器)生成这个序列...
2Q 算法和 LRU-2 算法命中率类似,内存消耗也比较接近,但对于最后缓存的数据来说,2Q 会减少一次从原始存储读取数据或者计算数据的操作。 10. Multi Queue(MQ)算法 MQ 算法根据访问频率将数据划分为多个队列,...
LRU(Least Recently Used)算法是一种常见的页面置换算法,它基于“最近最少使用的页面最可能在不久的将来再次被访问”的假设。在请求页式存储管理中,由于内存(主存)有限,不能一次性装入所有的虚拟页面,因此...
4. **性能分析**:LRU算法的性能可以通过页面命中率来衡量,即成功找到页面的次数占总访问次数的比例。在最佳情况下,如果每次访问的页面都是不同的,LRU的命中率与最优页面替换算法相同;但在最坏情况下,如果请求...
在这段代码中,实现了三种不同的页面置换算法:FIFO(先进先出)、LRU(最近最少使用)和 OPT(最佳置换算法)。下面将详细介绍这三种算法的基本原理、应用场景以及在这段代码中的具体实现方式。 ### 一、FIFO...
### LRU算法仿真:深入解析与C语言实现 在计算机科学领域,LRU(Least Recently Used,最近最少使用)算法是一种广泛应用于操作系统内存管理和高速缓存管理中的页面调度策略。其核心思想是当缓存满时,优先淘汰最近...
操作系统中的LRU(Least Recently Used)页面置换算法是内存管理的一种关键策略,它在处理物理内存有限而程序需要访问的页面数量超过内存容量时发挥作用。LRU算法的基本思想是:最近最少使用的页面最有可能在未来一...
(3)Void LRU( ):计算使用 LRU 算法时的命中率. (4)Void OPT( ):计算使用 OPT 算法时的命中率. (5)Void LFU( ):计算使用 LFU 算法时的命中率. (6)Void NUR( ):计算使用 NUR 算法时的命中率. 3.变量定义 (1)int a...
LRU(Least Recently Used)页面置换算法是一种在操作系统中用于管理内存资源的策略,它遵循一个简单的原则:当内存满时,最近最久未使用的页面将被替换出去。这个算法假设最近频繁使用的数据将来也更可能被频繁访问...
LRU算法实现 LRU(Least Recently Used)算法是一种常用的缓存淘汰算法...LRU算法是一种常用的缓存淘汰算法,能够有效地提高缓存命中率。但是,在实际应用中,需要根据具体情况选择合适的淘汰算法,以达到最佳的性能。
LRU(Least Recently Used)算法是一种常用的页面替换算法,用于解决计算机内存有限而数据需求量大时的问题。在操作系统中,当物理内存不足以存放所有正在使用的程序或数据时,操作系统会借助虚拟内存(如磁盘空间)...
LRU(Least Recently Used)最近最少使用替换算法是计算机系统结构中用于缓存管理的一种常见策略,它在内存有限而数据请求频繁的场景下尤为重要。LRU的基本思想是:当内存满时,最近最少使用的页面将被优先淘汰,以...