LRU算法
1、LRU算法(Least Recrntly Used 最近最少被使用算法):在一些内存算法中,表现为在一个内存有限的环境中,若出现内存不足的现象为了保证程序的正确运行,必须要抛弃一些价值不足的内存页,即抛弃一些价值较低的对象以获得内存保证程序的正确运行(如何确定一个对象价值的高低,LRU算法强调的是根据最近最少被使用的原则进行相应的舍弃(即认为该内存页价值不足))对于LRU算法来说:如果一个数据最近被访问到,那么数据在将来的一段时间也很有可能被访问到
程序的局部性原理(Local Principle of Program):程序的局部性原理是指程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。
局部性原理又表现为:时间局部性和空间局部性。
时间局部性是指如果程序中的某条指令一旦执行,则不久之后该指令可能再次被执行;如果某数据被访问,则不久之后该数据可能再次被访问。(循环语句)
空间局部性是指一旦程序访问了某个存储单元,则不久之后。其附近的存储单元也将被访问。(数组)
计算机操作系统基础理论中有一个经典理论:最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。
基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到最久未使用的数据然后置换出来,从而存入新的数据!它的主要衡量指标是使用的时间,附加指标是使用的次数。在计算机中大量使用了这个机制,它的合理性在于优先筛选热点数据,所谓热点数据,就是最近最多使用的数据!
2、LRU算法的应用实现
在缓存应用中,使用LRU算法可以保证缓存数据量的可控,一旦缓存数据超过一定的阈值,就将最近最少被访问的数据给清除掉、
3、实现LRU算法的不同方法
1.用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存在的数据项的时间戳自增,并将新数据项的时间戳置为0并插入到数组中。每次访问数组中的数据项的时候,将被访问的数据项的时间戳置为0。当数组空间已满时,将时间戳最大的数据项淘汰。
2.利用一个链表来实现,每次新插入数据的时候将新数据插到链表的头部;每次缓存命中(即数据被访问),则将数据移到链表头部;那么当链表满的时候,就将链表尾部的数据丢弃。
3.利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部,如果不存在,则新建一个节点,放到链表头部,若缓存满了,则把链表最后一个节点删除即可。在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1。这样一来在链表尾部的节点就是最近最久未访问的数据项。
对于第一种方法,需要不停地维护数据项的访问时间戳,另外,在插入数据、删除数据以及访问数据时,时间复杂度都是O(n)。对于第二种方法,链表在定位数据的时候时间复杂度为O(n)。所以在一般使用第三种方式来是实现LRU算法。
4、java中cache的实现(HashMap加双向链表来实现)
- HashMap的k,v结构对应这cache中k,v的概念,所以使用HashMap中V存储双向链表中的Node
- HashMap中查询写入的时间复杂度为O(1)
- 双向链表中的节点的移动,删除的时间复杂度为O(1)。
- 双向链表可以使我们通过任意一个节点获取其它所有节点