内存管理-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. 操作系统原理 陈向群
目录
相关文章
|
8天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
34 3
|
8天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
26 2
|
5天前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
35 16
|
18天前
|
Linux 应用服务中间件 Shell
linux系统服务二!
本文详细介绍了Linux系统的启动流程,包括CentOS 7的具体启动步骤,从BIOS自检到加载内核、启动systemd程序等。同时,文章还对比了CentOS 6和CentOS 7的启动流程,分析了启动过程中的耗时情况。接着,文章讲解了Linux的运行级别及其管理命令,systemd的基本概念、优势及常用命令,并提供了自定义systemd启动文件的示例。最后,文章介绍了单用户模式和救援模式的使用方法,包括如何找回忘记的密码和修复启动故障。
39 5
linux系统服务二!
|
18天前
|
Linux 应用服务中间件 Shell
linux系统服务!!!
本文详细介绍了Linux系统(以CentOS7为例)的启动流程,包括BIOS自检、读取MBR信息、加载Grub菜单、加载内核及驱动程序、启动systemd程序加载必要文件等五个主要步骤。同时,文章还对比了CentOS6和CentOS7的启动流程图,并分析了启动流程的耗时。此外,文中还讲解了Linux的运行级别、systemd的基本概念及其优势,以及如何使用systemd管理服务。最后,文章提供了单用户模式和救援模式的实战案例,帮助读者理解如何在系统启动出现问题时进行修复。
38 3
linux系统服务!!!
|
2天前
|
Ubuntu Linux 网络安全
linux系统ubuntu中在命令行中打开图形界面的文件夹
在Ubuntu系统中,通过命令行打开图形界面的文件夹是一个高效且实用的操作。无论是使用Nautilus、Dolphin还是Thunar,都可以根据具体桌面环境选择合适的文件管理器。通过上述命令和方法,可以简化日常工作,提高效率。同时,解决权限问题和图形界面问题也能确保操作的顺利进行。掌握这些技巧,可以使Linux操作更加便捷和灵活。
10 3
|
8天前
|
安全 网络协议 Linux
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。
本文详细介绍了 Linux 系统中 ping 命令的使用方法和技巧,涵盖基本用法、高级用法、实际应用案例及注意事项。通过掌握 ping 命令,读者可以轻松测试网络连通性、诊断网络问题并提升网络管理能力。
30 3
|
11天前
|
安全 Linux 数据安全/隐私保护
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。
在 Linux 系统中,查找文件所有者是系统管理和安全审计的重要技能。本文介绍了使用 `ls -l` 和 `stat` 命令查找文件所有者的基本方法,以及通过文件路径、通配符和结合其他命令的高级技巧。还提供了实际案例分析和注意事项,帮助读者更好地掌握这一操作。
30 6
|
11天前
|
Linux
在 Linux 系统中,`find` 命令是一个强大的文件查找工具
在 Linux 系统中,`find` 命令是一个强大的文件查找工具。本文详细介绍了 `find` 命令的基本语法、常用选项和具体应用示例,帮助用户快速掌握如何根据文件名、类型、大小、修改时间等条件查找文件,并展示了如何结合逻辑运算符、正则表达式和排除特定目录等高级用法。
39 6
|
13天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
35 6
下一篇
无影云桌面