内存管理-Linux伙伴系统-Java实现

简介: 内存 管理 Linux 伙伴系统 Java 实现

概念

一种经典的内存分配方案。
代码地址: github
https://github.com/fofcn/operation-system/tree/main/%E5%AE%9E%E8%B7%B5/3%20%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/buddy

主要思想

  1. 将内存按照2的幂进行划分,组成若干个空闲块链表;
  2. 查找该链表找到满足进程需要的最佳匹配块

算法

  1. 首先将真个可用空间看做一块:2^U
  2. 假设进程申请的空间大小为s,如果满足 2^u-1 < s <= 2^u则分配整个块;否则将块划分为两个大小相等的伙伴,大小为2^u-1
  3. 一直划分知道产生大于或等于s的最小块

图例

代码实现

public class BuddySystem {
    /**
     * 申请释放锁
     */
    private Lock lock = new ReentrantLock();
    private LinkedList<MemoryBlock> mmBlockList = new LinkedList<>();
    /**
     * 可用空闲区全部大小
     */
    private final int size;
    /**
     * 空闲区可用大小
     */
    private AtomicInteger idleSize = new AtomicInteger(0);

    /**
     * 内存块数量
     */
    private AtomicInteger blockSize = new AtomicInteger(0);

    /**
     * 是否初始化完成
     */
    private volatile boolean isInitialized = false;

    public BuddySystem(int size) {
        this.size = size;
        this.idleSize.set(size);
    }

    /**
     * 初始化伙伴系统内存链表
     */
    public void initialize() {
        if (isInitialized) {
            return;
        }
        MemoryBlock memoryBlock = new MemoryBlock();
        memoryBlock.setSize(size);
        memoryBlock.setStart(0);
        memoryBlock.setEnd(size - 1);
        mmBlockList.add(memoryBlock);
        blockSize.incrementAndGet();
        isInitialized = true;
    }

    /**
     * 分配内存
     * @param expectSize 待分配的内存大小
     */
    public MemoryBlock alloc(int expectSize) {
        // 参数检查
        if (expectSize > size) {
            throw new IllegalArgumentException("Sorry, I don't have enough memory.");
        }
        
        MemoryBlock best = null;
        
        // 开始查找最适合的块
        if (lock.tryLock()) {
            try {
                // 查看当前空闲块链表,找到小且大于等于申请大小的空闲块
                MemoryBlock smallestFit = findSmallestFitBlock(expectSize);
                if (smallestFit == null) {
                    throw new InternalError("not enough memory");
                }
                // 检查是否满足2^u-1 < expectSize <= 2^u
                // 满足则直接分配
                // 不满足则拆分为两个大于或等于S的小块,然后检查是否满足2^u-1 < size <= 2^u
                if (smallestFit.getSize() != expectSize) {
                    // 检查并等分内存区
                    while (expectSize * 2 <= smallestFit.getSize()) {
                        // 拆分
                        int smallBlockSize = smallestFit.getSize() / 2;
                        int start = smallestFit.getStart();
                        int middle = smallestFit.getStart() + smallBlockSize;
                        int end = smallestFit.getEnd();
                        MemoryBlock left = new MemoryBlock(start, middle, smallBlockSize, false);
                        MemoryBlock right = new MemoryBlock(middle, end, smallBlockSize, false);
                        // 在删除点添加这两块内存
                        mmBlockList.replace(smallestFit, left, right);
                        blockSize.incrementAndGet();
                        smallestFit = left;
                    }
                }

                best = smallestFit;
                best.setUsed(true);
                idleSize.addAndGet(-best.getSize());
            } finally {
                lock.unlock();
            }
        }

        return best;
    }

    private MemoryBlock findSmallestFitBlock(int expectSize) {
        MemoryBlock smallestFit = null;
        for (MemoryBlock memoryBlock : mmBlockList) {
            if (!memoryBlock.isUsed() &&
                    memoryBlock.getSize() >= expectSize) {
                if (smallestFit == null) {
                    smallestFit = memoryBlock;
                } else {
                    if (smallestFit.getSize() > memoryBlock.getSize()) {
                        smallestFit = memoryBlock;
                    }
                }
            }
        }
        return smallestFit;
    }

    public void free(MemoryBlock freeBlock) {
        List<LinkedList.Node<MemoryBlock>> mergeNodes = new ArrayList<>(3);
        // 释放内存需要检查四种相邻场景
        // 如果有相邻,则合并
        if (lock.tryLock()) {
            try {
                freeBlock.setUsed(false);

                LinkedList.Node<MemoryBlock> toFreeNode = mmBlockList.findNode(freeBlock);
                if (toFreeNode == null) {
                    throw new IllegalArgumentException("You accessed memory illegally.");
                }

                // 检查前一个节点
                // 检查查找到的块是否大小相等且地址连续
                LinkedList.Node<MemoryBlock> prev = toFreeNode.prev;
                LinkedList.Node<MemoryBlock> next = toFreeNode.next;
                if (prev != null && !prev.item.isUsed()
                        && prev.item.getSize() == toFreeNode.item.getSize()
                        && prev.item.getEnd() == toFreeNode.item.getStart()) {
                    mergeNodes.add(prev);
                    blockSize.incrementAndGet();
                }

                mergeNodes.add(toFreeNode);

                // 检查下一个节点
                // 检查查找到的块是否大小相等且地址连续
                if (next != null && !next.item.isUsed()
                        && next.item.getSize() == toFreeNode.item.getSize()
                        && next.item.getStart() == toFreeNode.item.getEnd()) {
                    mergeNodes.add(next);
                }

                int start = mergeNodes.get(0).item.getStart();
                int end = mergeNodes.get(mergeNodes.size() - 1).item.getEnd();
                int itemSize = 0;

                for (LinkedList.Node<MemoryBlock> node : mergeNodes) {
                    itemSize += node.item.getSize();
                    blockSize.decrementAndGet();
                }


                // 删除当前节点和下一个节点,然后更新第一个节点大小和连接
                mmBlockList.remove(prev);
                mmBlockList.remove(next);
                toFreeNode.item.setStart(start);
                toFreeNode.item.setEnd(end);
                toFreeNode.item.setSize(itemSize);
            } finally {
                lock.unlock();
            }
        }
    }

    public void printBlocks() {
        int index = 1;
        for (MemoryBlock memoryBlock : mmBlockList) {
            StdOut.println("index: " + index + " " + memoryBlock);
            index++;
        }
    }

    public int getSize() {
        return size;
    }

    public int getIdleSize() {
        return idleSize.get();
    }

    public int getBlockSize() {
        return blockSize.get();
    }
}

测试用例

public class BuddySystemTest {

    private BuddySystem buddySystem;
    private static final int MAX_MEM_SIZE = 1024 * 1024 * 1024;

    @Before
    public void before() {
        buddySystem = new BuddySystem(MAX_MEM_SIZE);
    }

    @Test
    public void testInitialize() {
        buddySystem.initialize();
        Assert.assertEquals(MAX_MEM_SIZE, buddySystem.getSize());
        Assert.assertEquals(0, buddySystem.getIdleSize());
    }

    @Test
    public void testAllocAndFree() {
        buddySystem.initialize();

        // 分配100KB
        int size = 100 * 1024 * 1024;
        MemoryBlock block = buddySystem.alloc(size);
        Assert.assertNotNull(block);
        Assert.assertEquals(MAX_MEM_SIZE - block.getSize(), buddySystem.getIdleSize());
        Assert.assertEquals(buddySystem.getBlockSize(), 4);
        buddySystem.printBlocks();
        System.out.println("=================================");
        System.out.println("=================================");

        // 分配240KB
        size = 240 * 1024 * 1024;
        MemoryBlock block2 = buddySystem.alloc(size);
        Assert.assertNotNull(block2);
        Assert.assertEquals(MAX_MEM_SIZE - block.getSize() - block2.getSize(), buddySystem.getIdleSize());
        Assert.assertEquals(buddySystem.getBlockSize(), 4);
        buddySystem.printBlocks();
        System.out.println("=================================");
        System.out.println("=================================");

        // 分配64KB
        size = 64 * 1024 * 1024;
        MemoryBlock block3 = buddySystem.alloc(size);
        Assert.assertNotNull(block3);
        Assert.assertEquals(MAX_MEM_SIZE - block.getSize() - block2.getSize() - block3.getSize(), buddySystem.getIdleSize());
        Assert.assertEquals(buddySystem.getBlockSize(), 5);
        buddySystem.printBlocks();
        System.out.println("=================================");
        System.out.println("=================================");

        // 分配256KB
        size = 256 * 1024 * 1024;
        MemoryBlock block4 = buddySystem.alloc(size);
        Assert.assertNotNull(block3);
        Assert.assertEquals(MAX_MEM_SIZE - block.getSize() - block2.getSize()
                - block3.getSize() - block4.getSize(), buddySystem.getIdleSize());
        Assert.assertEquals(buddySystem.getBlockSize(), 6);
        buddySystem.printBlocks();
        System.out.println("=================================");
        System.out.println("=================================");

        // 释放240KB
        buddySystem.free(block2);
        Assert.assertEquals(buddySystem.getBlockSize(), 5);
    }
}

参考

  1. 操作系统原理 陈向群
目录
相关文章
|
2天前
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
38 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
2月前
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
214 78
|
23天前
|
缓存 安全 Linux
Linux系统查看操作系统版本信息、CPU信息、模块信息
在Linux系统中,常用命令可帮助用户查看操作系统版本、CPU信息和模块信息
86 23
|
15天前
|
JavaScript Java 测试技术
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
39 6
|
24天前
|
存储 分布式计算 Hadoop
基于Java的Hadoop文件处理系统:高效分布式数据解析与存储
本文介绍了如何借鉴Hadoop的设计思想,使用Java实现其核心功能MapReduce,解决海量数据处理问题。通过类比图书馆管理系统,详细解释了Hadoop的两大组件:HDFS(分布式文件系统)和MapReduce(分布式计算模型)。具体实现了单词统计任务,并扩展支持CSV和JSON格式的数据解析。为了提升性能,引入了Combiner减少中间数据传输,以及自定义Partitioner解决数据倾斜问题。最后总结了Hadoop在大数据处理中的重要性,鼓励Java开发者学习Hadoop以拓展技术边界。
39 7
|
2月前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
82 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
2月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
116 13
|
2月前
|
存储 监控 算法
Java内存管理深度剖析:从垃圾收集到内存泄漏的全面指南####
本文深入探讨了Java虚拟机(JVM)中的内存管理机制,特别是垃圾收集(GC)的工作原理及其调优策略。不同于传统的摘要概述,本文将通过实际案例分析,揭示内存泄漏的根源与预防措施,为开发者提供实战中的优化建议,旨在帮助读者构建高效、稳定的Java应用。 ####
51 8
|
2月前
|
机器学习/深度学习 人工智能 缓存
【AI系统】推理内存布局
本文介绍了CPU和GPU的基础内存知识,NCHWX内存排布格式,以及MNN推理引擎如何通过数据内存重新排布进行内核优化,特别是针对WinoGrad卷积计算的优化方法,通过NC4HW4数据格式重排,有效利用了SIMD指令集特性,减少了cache miss,提高了计算效率。
65 3
|
2月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。