使用java.util.LinkedList模拟实现内存页面置换算法--LRU算法

简介:

一,LRU算法介绍

LRU是内存分配中“离散分配方式”之分页存储管理方式中用到的一个算法。每个进程都有自己的页表,进程只将自己的一部分页面加载到内存的物理块中,当进程在运行过程中,发现某页面不在物理内存块中(发生缺页异常)就需要从磁盘把相应的页面调入内存。而若内存已经满了的情况下,需要将内存中暂时不用的物理块页面 换出到磁盘(交换空间)中,那到底换出哪一页呢?LRU算法就是用来解决到底换出哪一页 的这个问题。

LRU算法是最近最少未使用算法。当内存缺页时,总是优先选出距离当前最久未使用的页面换出,并把当前的缺页换入。该算法可用栈模拟实现。

栈顶总是保存当前最近访问的页面号,栈底则总是保存最久未访问的页面号。对于下一个页面,有两种情况:

①命中,则需要:更新栈顶元素。即将当前命中的页面号放到栈顶。

②未命中,这里还需要考虑栈是否满了。1)若栈未满,直接将未命中的页面号放到栈顶即可。为什么要放到栈顶(LinkedList表头)呢?

因为,LRU每次总是选取最近最久未被访问的页面淘汰。某页面刚刚被访问,需要放到栈顶,以表示它不是“最近最久 未访问的页面”

2)栈已经满了,则需要选中一页换出(栈底元素是最久未访问的页面),然后再将新页面放入栈顶

 

二,代码实现

复制代码
import java.util.LinkedList;

public class LRU {

    private LinkedList<Integer> stack;//模拟页面'寄存器'
    private int size;//寄存器大小,表示一共可装入多少页面
    
    public LRU(int size) {
        stack = new LinkedList<>();
        this.size = size;
    }

    //LRU算法简单实现,返回一共未命中的次数
    public int lru(int[] pageNumbers)
    {
        if(size <= 0 || pageNumbers == null)
            throw new IllegalArgumentException("illegal arugments");
        
        if(pageNumbers.length <= size)
            return pageNumbers.length;
        
        int unhit = 0;
            
        for(int i = 0; i < pageNumbers.length; i++)
        {
            int index = isHit(pageNumbers[i]);
            if(index == -1)
                unhit = processUnHit(pageNumbers[i], unhit);
            else
            {
                processHit(pageNumbers[i], index);
            }
        }
        return unhit;
    }
    
    /**
     * 
     * @param pageNumber 判断 pageNumber是否hit
     * @return -1 表示 unhit, 其他表示hit
     */
    private int isHit(int pageNumber){
        return stack.indexOf(pageNumber);
    }
    
    /**
     * 当栈未满时,未命中的页面号直接入栈;栈满时,需要替换页面,先选中一个页面(栈底)删除,然后Push新页面
     * @param pageNumber 未命中的页面号
     * @param count 当前未命中次数
     * @return 更新后的未命中的次数
     */
    private int processUnHit(int pageNumber, int count){
        if(isFull())
            stack.removeLast();//删除最久未访问的页面
        stack.push(pageNumber);//放入最近访问的页面
        count++;//未命中的次数加1
        return count;
    }
    
    //处理命中的情况
    private void processHit(int pageNumber, int index){
            stack.push(stack.remove(index));
    }
    
    //判断'寄存器'栈是否已经满了
    private boolean isFull()
    {
        if(stack.size() < size)
            return false;
        else
            return true;
    }
    
    //test
    public static void main(String[] args) {
        int[] pageNumbers = {4,7,1,1,7,2,1};
        int size = 2;
        LRU lru = new LRU(size);
        System.out.println(lru.lru(pageNumbers));
    }
}
复制代码

 

三,复杂度分析

由于java.util.LinkedList 实现了栈的功能。push()方法总是将元素放到表头,pop()方法总是从链表的表头删除元素。在这里,链表的表头代表栈顶。

因此,当某页面号命中时,需要从链表中找到该页面的位置(index),然后删除该页面,并将它push到链表的表头。---processHit()方法

由于是链表,故寻找某页面的时间复杂度为O(N),最坏情况下扫描整个链表。

当页面未命中的,需要将页面号push到链表表头。push之前,先检查栈是否已经满了。若未满,直接push入栈,时间复杂度为O(1);如果栈已经满了,需要删除链表的表尾元素(相当于栈底元素--removeLast()时间复杂度也为O(1),因为LinkedList本质上是一个双向链表。)然后,再将该删除的元素push到栈顶--时间复杂度为O(1)

故未命中时,处理的总的时间复杂度还是O(1)


本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/5687768.html,如需转载请自行联系原作者

相关文章
|
8月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
973 3
|
9月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
7月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
205 4
|
7月前
|
存储 缓存 Java
【深入浅出】揭秘Java内存模型(JMM):并发编程的基石
本文深入解析Java内存模型(JMM),揭示synchronized与volatile的底层原理,剖析主内存与工作内存、可见性、有序性等核心概念,助你理解并发编程三大难题及Happens-Before、内存屏障等解决方案,掌握多线程编程基石。
|
8月前
|
存储 缓存 NoSQL
工作 10 年!Redis 内存淘汰策略 LRU 和传统 LRU 差异,还傻傻分不清
小富带你深入解析Redis内存淘汰机制:LRU与LFU算法原理、实现方式及核心区别。揭秘Redis为何采用“近似LRU”,LFU如何解决频率老化问题,并结合实际场景教你如何选择合适策略,提升缓存命中率。
1155 3
|
8月前
|
缓存 监控 Kubernetes
Java虚拟机内存溢出(Java Heap Space)问题处理方案
综上所述, 解决Java Heap Space溢出需从多角度综合施策; 包括但不限于配置调整、代码审查与优化以及系统设计层面改进; 同样也不能忽视运行期监控与预警设置之重要性; 及早发现潜在风险点并采取相应补救手段至关重要.
947 17
|
9月前
|
监控 Kubernetes Java
最新技术栈驱动的 Java 绿色计算与性能优化实操指南涵盖内存优化与能效提升实战技巧
本文介绍了基于Java 24+技术栈的绿色计算与性能优化实操指南。主要内容包括:1)JVM调优,如分代ZGC配置和结构化并发优化;2)代码级优化,包括向量API加速数据处理和零拷贝I/O;3)容器化环境优化,如K8s资源匹配和节能模式配置;4)监控分析工具使用。通过实践表明,这些优化能显著提升性能(响应时间降低40-60%)同时降低资源消耗(内存减少30-50%,CPU降低20-40%)和能耗(服务器功耗减少15-35%)。建议采用渐进式优化策略。
484 2
|
10月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
517 0
|
9月前
|
存储 监控 算法
Java垃圾回收机制(GC)与内存模型
本文主要讲述JVM的内存模型和基本调优机制。
|
9月前
|
边缘计算 算法 Java
Java 绿色计算与性能优化:从内存管理到能耗降低的全方位优化策略与实践技巧
本文探讨了Java绿色计算与性能优化的技术方案和应用实例。文章从JVM调优(包括垃圾回收器选择、内存管理和并发优化)、代码优化(数据结构选择、对象创建和I/O操作优化)等方面提出优化策略,并结合电商平台、社交平台和智能工厂的实际案例,展示了通过Java新特性提升性能、降低能耗的显著效果。最终指出,综合运用这些优化方法不仅能提高系统性能,还能实现绿色计算目标,为企业节省成本并符合环保要求。
298 0

热门文章

最新文章