LFU vs LRU

LFU 最近最少使用优先

Least Frequently Used ,淘汰一段时间内使用次数最少的页面。

LRU 最久未使用

Least Recently Used ,淘汰最长时间没有使用的页面。

练习题

假设LFU方法的时期T为10分钟,访问如下页面的时间正好为10分钟,内页块大小为3,页面顺序: 2 1 2 1 2 3 4 ,当需要使用4页面时,内存块中没有页面4,就会发生缺页中断,而且此时内存块已满,需要进行页面转换。

次数 LRU LFU
1 2 2(1)
2 2,1 2(1),1(1)
3 1,2 2(2),1(1)
4 2, 1 2(2),1(2)
5 1, 2 2(3),1(2)
6 1, 2 , 3 2(3),1(2),3(1)
7 2, 3, 4 (置换出1) 2(3),1(2),4(1) 3使用次数最少,优先置换出3

FIFO 先进先出

First in First out, 队列

代码

LFU

  1. package cn.com.cninfo.reply;
  2. import java.util.Hashtable;
  3. /**
  4. * @author chenxinwei
  5. * @date 2021/7/23 18:11
  6. **/
  7. public class LFUCache {
  8. class DNode {
  9. DNode prev, next;
  10. int key, value, count;
  11. @Override
  12. public String toString() {
  13. return "DNode key[" + key + "] value[" + value + ']';
  14. }
  15. }
  16. //添加到头部
  17. private void addNodeFrist(DNode node) {
  18. node.next = head.next;
  19. node.prev = head;
  20. head.next = node;
  21. node.next.prev = node;
  22. }
  23. //移除node
  24. private void removeNode(DNode node) {
  25. node.prev.next = node.next;
  26. node.next.prev = head;
  27. }
  28. //将某个Node移到头部
  29. private void removeNodeToHead(DNode node) {
  30. removeNode(node);
  31. addNodeFrist(node);
  32. }
  33. //查找访问频率最低的节点进行移除
  34. private DNode removeLastNode() {
  35. DNode pNode = head.next;
  36. DNode lowVisitNode = head.next;
  37. int lowVisitTime = tail.prev.count;
  38. while (pNode != null && pNode.count > 0) {
  39. if (lowVisitTime >= pNode.count) {
  40. lowVisitTime = pNode.count;
  41. lowVisitNode = pNode;
  42. }
  43. lowVisitNode = lowVisitNode.next;
  44. }
  45. removeNode(lowVisitNode);
  46. return lowVisitNode;
  47. }
  48. private int capacity, size;
  49. private Hashtable<Integer, DNode> cache;
  50. private DNode head, tail;
  51. public LFUCache(int capacity) {
  52. this.capacity = capacity;
  53. this.cache = new Hashtable<>(capacity);
  54. this.size = 0;
  55. head = new DNode();
  56. tail = new DNode();
  57. head.next = tail;
  58. tail.prev = head;
  59. }
  60. public void put(int key, int value) {
  61. DNode node = cache.get(key);
  62. if (node == null) {
  63. DNode newNode = new DNode();
  64. newNode.key = key;
  65. newNode.value = value;
  66. cache.put(key, newNode);
  67. addNodeFrist(newNode);
  68. size++;
  69. if (size > capacity) {
  70. DNode res = removeLastNode();
  71. cache.remove(res.key);
  72. size--;
  73. }
  74. } else {
  75. node.value = value;
  76. removeNodeToHead(node);
  77. }
  78. }
  79. public int get(int key) {
  80. DNode node = cache.get(key);
  81. if (node == null) {
  82. return -1;
  83. }
  84. removeNodeToHead(node);
  85. return node.value;
  86. }
  87. public static void main(String[] args) {
  88. LFUCache cache = new LFUCache(3);
  89. cache.put(2, 2);
  90. cache.put(1, 1);
  91. System.out.println(cache.get(2));
  92. System.out.println(cache.get(1));
  93. System.out.println(cache.get(2));
  94. cache.put(3, 3);
  95. cache.put(4, 4);
  96. //1、2元素都有访问次数,放入3后缓存满,加入4时淘汰3
  97. System.out.println(cache.get(3));
  98. System.out.println(cache.get(2));
  99. //System.out.println(cache.get(1));
  100. System.out.println(cache.get(4));
  101. cache.put(5, 5);
  102. //目前2访问2次,1访问一次,4访问一次,由于4的时间比较新,放入5的时候移除1元素。
  103. System.out.println("=============================");
  104. cache.cache.entrySet().forEach(entry -> {
  105. System.out.println(entry.getValue());
  106. });
  107. }
  108. }
  109. // cache的容量为:1
  110. // cache的容量为:2
  111. // cache的容量为:3
  112. // cache的容量为:4
  113. // cache的容量为:5
  114. // cache的容量为:5
  115. // cache的容量为:5
  116. // cache的容量为:5
  117. // cache的容量为:5
  118. // cache的容量为:5
  119. // cache的容量为:5
  120. // =-=-=-=-=-=-=-
  121. // map元素:
  122. // 7
  123. // 8
  124. // 9
  125. // 10
  126. // 11

LinkedHashMap

2. 内存淘汰算法 - 图1

LinkedHashMap采用数组 + 单身链表的形式,只是在节点Entry中增加了Beforeafter变量,用于维护双向链表保存LinkedHashMap的存储顺序。这个双向链表提供了两种排序方法,插入顺序(accessOrderfalse时)和访问顺序(accessOrdertrue时)。

LRU

package cn.com.cninfo.reply;

import java.util.Hashtable;

/**
 * @author chenxinwei
 * @date 2021/7/23 18:11
 **/
public class LFUCache {
    class DNode {
        DNode prev, next;
        int key, value, count;

        @Override
        public String toString() {
            return "DNode key[" + key + "] value[" + value + ']';
        }
    }

    //添加到头部
    private void addNodeFrist(DNode node) {
        node.next = head.next;
        node.prev = head;
        head.next = node;
        node.next.prev = node;
    }

    //移除node
    private void removeNode(DNode node) {
        node.prev.next = node.next;
        node.next.prev = head;
    }

    //将某个Node移到头部
    private void removeNodeToHead(DNode node) {
        removeNode(node);
        addNodeFrist(node);
    }

    //查找访问频率最低的节点进行移除
    private DNode removeLastNode() {
        DNode pNode = head.next;
        DNode lowVisitNode = head.next;
        int lowVisitTime = tail.prev.count;
        while (pNode != null && pNode.count > 0) {
            if (lowVisitTime >= pNode.count) {
                lowVisitTime = pNode.count;
                lowVisitNode = pNode;
            }
            lowVisitNode = lowVisitNode.next;
        }
        removeNode(lowVisitNode);
        return lowVisitNode;
    }

    private int capacity, size;
    private Hashtable<Integer, DNode> cache;
    private DNode head, tail;

    public LFUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new Hashtable<>(capacity);
        this.size = 0;
        head = new DNode();
        tail = new DNode();
        head.next = tail;
        tail.prev = head;
    }

    public void put(int key, int value) {
        DNode node = cache.get(key);
        if (node == null) {
            DNode newNode = new DNode();
            newNode.key = key;
            newNode.value = value;
            cache.put(key, newNode);

            addNodeFrist(newNode);
            size++;

            if (size > capacity) {
                DNode res = removeLastNode();
                cache.remove(res.key);
                size--;
            }
        } else {
            node.value = value;
            removeNodeToHead(node);
        }
    }

    public int get(int key) {
        DNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        removeNodeToHead(node);
        return node.value;
    }
    public static void main(String[] args) {
        LFUCache cache = new LFUCache(3);

        cache.put(2, 2);
        cache.put(1, 1);

        System.out.println(cache.get(2));
        System.out.println(cache.get(1));
        System.out.println(cache.get(2));

        cache.put(3, 3);
        cache.put(4, 4);

        //1、2元素都有访问次数,放入3后缓存满,加入4时淘汰3
        System.out.println(cache.get(3));
        System.out.println(cache.get(2));
        //System.out.println(cache.get(1));
        System.out.println(cache.get(4));

        cache.put(5, 5);
        //目前2访问2次,1访问一次,4访问一次,由于4的时间比较新,放入5的时候移除1元素。
        System.out.println("=============================");
        cache.cache.entrySet().forEach(entry -> {
            System.out.println(entry.getValue());
        });
    }
}
//    2
//    1
//    2
//    3
//    2
//    -1
//    =============================
//    DNode key[3] value[3]
//    DNode key[2] value[2]
//    DNode key[1] value[1]