java lru 算法实现_LRU 算法的原理及实现

什么是 LRU ?

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

基于分值实现的 LRU 分析

class Item:

key = None

value = None

counter = 0

class LRUCache(dict):

capacity = 100

threshold = 0.5

_counter = 0

capacity 绝对有效内存(一定不会被回收), capacity + threshold * capacity 超出则触发缓存内存回收,回收 [capacity:] 部分, 设计 threshold * capacity 是为了避免频繁内存回收。

_counter 内容访问记录逻辑次数(非实际访问次)基值, 每次访问基值都会自增 1,保证最近访问的内容的 _counter 值是最大。(如下)

def _counter_inc(self):

self._counter += 1

return self._counter

获取缓存值时,更新其 _counter 值,如下

def get(self, key):

item = dict.__getitem__(self, key)

if item:

item.counter = self._counter(self)

return item.value

else:

return None

插入缓存值,校验内存空间,若超出内存空间,则触发内存空间回收,代码如下:

def set(self, key, value):

item = dict.__getitem__(self, key)

if item:

item.value = value

item.counter = self._counter

else:

item = Item(key=key, value=value, counter=self._counter())

dict.__setitem__(self, key, item)

if len(self) > capacity + capacity * threshold:

items = sorted(dict.values(self), key=lamaba x: x.counter, reverse=True)

for item in items[capacity:]:

del self[item.key]

基于双向链表实现的 LRU 分析(草稿)

class Node:

before = None

after = None

key = None

value = None

class LRUCache(dict):

capacity = 100

threshold = 0.5

head = None

tail = None

访问缓存值时,将最新被访问的数据移到链表头部,代码如下:

def get(self, key):

item = dict.__getitem__(self, key)

if item:

return None

afterNodeAccess(item)

return item

将最新访问数据移至链表头部,代码如下:

def afterNodeAccess(item):

if item == self.head:

return

before = item.before

after = item.after

head = self.head

tail = self.tail

self.head = item

item.after = head

head.before = item

before.after = after

if after:

after.before = before

else:

self.tail = before

插入缓存值,校验内存空间,若超出内存空间,则触发内存空间回收,代码如下:

def set(self, key, value):

item = dict.__getitem__(self, key)

if item:

item.value = value

else:

item = Item(key=key, value=value, before=None, after=None)

head = self.head

self.head = item

item.after = head

head.before = item

dict.__setitem__(self, key, item)

if len(self) > capacity + capacity * threshold:

for i in range(len(self) - capacity):

tail = self.tail

before = self.tail

before.after = None

self.tail = before

del self[tail.key]

Java 的 LinkedHashMap 实现了 LRU 算法,原理即是基于双向链表记录插入、访问的规则,在插入、访问元素时,变换链表。LinkedHashMap 默认插入作为排序,可以设定 accessOrder 为 True,来使得排序依据访问情况,具体内部实现逻辑与 HashMap 类似,可以根据插入、访问排序主要是重写了 newNode、afterNodeAccess 访问,该方法实现了对双向链表的操作,插入时将元素更新至链表尾部,访问时将数据更新至链表头部。

其它

LRU 算法弊端是存在偶发性、周期性的批量操会降低缓存的命中率,对缓存造成污染,下面几个就是改进算法。

LRU-K 会记录每条数据的访问历史,当达到 k 时,才将数据存放到缓存,在缓存内存回收时,缓存中越接近 k 的数据被优先删除。

Two queues(2Q)相当于 LRU-2,区别是访问历史(首次访问)数据缓存于 FIFO 队列,二次及以上的数据存放 LRU 缓存,FIFO 队列数据遵循该缓存的内存回收机制,LRU 缓存数据遵循该缓存的内存回收机制。

Multi Queue(MQ)该算法根据访问频率,分割出多个优先级别的缓存,访问频率越高的数据存放越高优先级的缓存中,各优先级根据访问频率增减数据在之间迁移,最低优先级的数据会被清除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值