美文网首页
LRU算法(内存管理)

LRU算法(内存管理)

作者: 宫若石 | 来源:发表于2019-03-10 18:27 被阅读0次

    LRU究竟是个什么东西呢,听上去是那么的高大上。Least Recently Used就是LRU的真面目,翻译过来是:最近最少使用。

    我们要在有限的内存中存放一些<K,V>键值对,这些键值对很多,所有的键值对所占内存大于物理可用内存,并且每个键值对被访问的情况也是不一样的。当内存用尽的时候,这时新来了一个键值对,这时我们要如何处理呢?从内存中删除“潜水”时间最长的那个键值对,这样就可以把新来的键值对存入内存了,这就是LRU算法,也是在Redis缓存丢弃策略的一种。上面说的“潜水”时间最长指的是:被人遗忘时间最长的那个键值对,遗忘指的是没有被人访问过。

    总结之下,LRU就是当你内存中数据到达指定容量的时候,LRU选择将最长时间没有被使用过的那个键值对从内存中移除。

    我们回顾一下要实现的功能,功能完成了,LRU也就实现了:

1. 我们需要存储<K,V>键值对。

2. 可以指定可以存储多少个键值对。

3. 当存储容量满了以后,移除最长时间没有被访问过的键值对。

分析一下上面的需求:

1. 存储键值对:第一想法是使用HashMap存储,毕竟HashMap是为“存储键值对而生”。

2. 指定容量:这也容易,每次向map中put键值对后,就检查map的容量是否超出了指定的容量,如果超出了,从map中移除一个元素即可。

3. 移除谁?难点在这里,当map在put操作之后超出了指定容量,我们移除谁?怎么找出那个“潜水”时间最长的键值对呢?第一个想法是我们需要维护一个有序的链表,这个链表从前到后“潜水”时间越来越短。这样当map的容量满了以后我们只需要获取链表头部元素,然后从map中移除该键值对即可,这也说明了链表节点中需要包含对应键值对的key,否则找到链表头结点后如何移除呢?

4. 为什么使用链表?容量指定的情况下,为什么不使用数组呢?原因如下,“潜水”时间最长的键值对经常发送变化,也就是链表中元素顺序经常发生改变,另外一方面,链表的插入和操作删除效率要比数组高。

5. 如何维护链表节点的顺序?可以这样,当一个“潜水”的键值对被访问后,把该节点从链表中移除,然后把这个节点插入到链表末尾,这样就可以保证链表第一个元素永远是“潜水”时间最长的键值对;链表末尾的元素永远是最近被访问过的元素。这样当map超出指定容量之后只需要删除链表头的键值对即可。

代码:

class LRUCache {

//“潜水”链表节点,抽象

  static class Node{

      //键值对

private int key;

private int value;

//维护“潜水”键值对,双向链表

private Node pre;

private Node next;

      //构造器

      Node(){}

Node(int key,int value){

this.key = key;

this.value = value;

}

}

//指定的容量

private int cap;

//保留“潜水”双向链表的头尾指针

private Node head;

private Node tail;

//保存键值对的map

private HashMap map;

//构造器参数是:指定的容量

public LRUCache(int capacity) {

this.cap = capacity;

//初始化头尾节点,这里的头结点是

//辅助节点

//head节点不存储任何有效元素

head = new Node();

tail = head;

//构造器初试容量这样设置可以保证map

//不会发生扩容,详见之前的HashMap

//讲解文章

map = new HashMap<>((int)                                    (cap/0.75)+1);

}

//将指定节点从链表中删除

private void removeNode(Node cur){

if(cur==tail){

tail = tail.pre;

tail.next = null;

cur.pre = null;

}else{

cur.pre.next = cur.next;

cur.next.pre = cur.pre;

cur.pre = null;

cur.next = null;

}

}

//将指定节点追加到链表末尾

private void add(Node cur){

tail.next = cur;

cur.pre = tail;

tail = cur;

}

//访问一个键值对

public int get(int key) {

Node cur = map.get(key);

//不存在这个key

if(cur==null){

return -1;

}else{//存在

    //含义是当前潜水节点已经被访问了

//将这个节点添加到链表末尾

removeNode(cur);

add(cur);

return cur.value;

}

//存储一个键值对

public void put(int key, int value) {

Node cur =  map.get(key);

if(cur==null){

//put前不存在这个key

cur = new Node(key,value);

//将该键值对移动到链表末尾

map.put(key,cur);

add(cur);

//超出了容量,移除链表头结点

//后面那个元素(头结点是辅助节点)

if(map.size()>cap && head!=tail){

Node outDate  = head.next;

removeNode(outDate);

//不能忘记这里                                    map.remove(outDate.key);

}

}else{

      //put之前已经存在

//将这个键值对移到链表末尾即可

removeNode(cur);

add(cur);

//更新这个key的值

cur.value = value;

}

}

}

最后LinkedHashMap其实是可以直接支持LRU的,面试的时候可以提及这一点,但是面试官不会支持你使用LinkedHashMap实现LRU。之后讲解LinkedHashMap源码的时候会有详细说明,实质上LinkedHashMap底层实现也是类似原理,使用LinkedHashMap实现LRU代码如下:

//继承一下LinkedHashMap这个类,

//使用LinkedHashMap实现LRU算法

class LRULinkedHashMap<K,V>

    extends LinkedHashMap<K,V>{

      //定义缓存的容量

      private int capacity;

      //带参数的构造器 

      LRULinkedHashMap

                    (int capacity){

      //调用LinkedHashMap的构造器

super(capacity,0.75f,true);

          //传入指定的缓存最大容量

          this.capacity=capacity;

      }

//返回true就会移除“潜水”时间最长

//的键值对

      @Override

  public boolean removeEldestEntry

        (Map.Entry<K, V> eldest){

//参数eldest就是“潜水”时间最长

//的键值对,可以获得对应的

//key,value

          return size()>capacity;

      } 

  }

相关文章

  • 缓存 -- LRU算法

    什么是LRU算法 LRU算法的全称Least Recently Used。即最近最少使用。LRU算法是内存管理的一...

  • LruCache介绍

    1. LRU。 LRU是Least Recently Used 近期最少使用算法。内存管理的一种页面置换算法,对于...

  • LRU算法/最近最久未使用算法 与Clock算法

    一 LRU算法 LRU算法在操作系统的内存管理,MySQL页管理,redis的缓存管理中都有使用到,这是一种通用的...

  • LinkedHashMap实现LRU原理解析

    LRU介绍 LRU是Least Recently Used 最近最少使用算法。是一种常用的内存管理的页面置换算法。...

  • LRU算法(内存管理)

    LRU究竟是个什么东西呢,听上去是那么的高大上。Least Recently Used就是LRU的真面目,翻译...

  • 四 redis内存淘汰策略思想

    目标 分析redis的内存淘汰策略, lru算法简介 lru算法简介 Least recently used(LR...

  • LRU算法原理与实践

    简介 操作系统中进行内存管理中时采用一些页面置换算法,如LRU、LFU和FIFO等。其中LRU应用较为广泛。LRU...

  • 计算LRU缺页中断次数

    内存管理中有一种页面置换算法叫最近最少使用(LRU)算法,编写程序模拟LRU的运行过程,依次输入分配给某进程的缓存...

  • LinkedHashMap源码浅析

    这是LRU算法的核心,比如Glide里无论是内存缓存还是硬盘缓存,其实核心都是用到了LRU算法,而LRU算法核心是...

  • Android - LruCache 内存缓存

    什么是 LruCache 是一种内存缓存对象,使用 LRU(Least Recent Used)算法管理缓存。 缓...

网友评论

      本文标题:LRU算法(内存管理)

      本文链接:https://www.haomeiwen.com/subject/kvrppqtx.html