内存管理-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的最小块

图例

![Image [2].png](https://ucc.alicdn.com/pic/developer-ecology/738f9d03179f426e82b6a145098dbfca.png)

代码实现

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. 操作系统原理 陈向群
目录
相关文章
|
6天前
|
运维 Java
Java版HIS系统 云HIS系统 云HIS源码 结构简洁、代码规范易阅读
云HIS系统分为两个大的系统,一个是基层卫生健康云综合管理系统,另一个是基层卫生健康云业务系统。基层卫生健康云综合管理系统由运营商、开发商和监管机构使用,用来进行运营管理、运维管理和综合监管。基层卫生健康云业务系统由基层医院使用,用来支撑医院各类业务运转。
30 5
|
5天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
2天前
|
Java 程序员 数据库连接
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
|
3天前
|
存储 安全 Java
滚雪球学Java(19):JavaSE中的内存管理:你所不知道的秘密
【4月更文挑战第8天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
29 4
滚雪球学Java(19):JavaSE中的内存管理:你所不知道的秘密
|
7天前
|
JavaScript Java 测试技术
基于Java的电影评论系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的电影评论系统的设计与实现(源码+lw+部署文档+讲解等)
23 0
|
7天前
|
JavaScript Java 测试技术
基于Java的实验室设备管理系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的实验室设备管理系统的设计与实现(源码+lw+部署文档+讲解等)
20 1
|
8天前
|
JavaScript Java 测试技术
基于Java的社区人员管理系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的社区人员管理系统的设计与实现(源码+lw+部署文档+讲解等)
26 2
|
8天前
|
JavaScript Java 测试技术
基于Java的公司员工工作日志办公系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的公司员工工作日志办公系统的设计与实现(源码+lw+部署文档+讲解等)
32 3
|
8天前
|
JavaScript Java 测试技术
基于Java的图书馆智能选座系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的图书馆智能选座系统的设计与实现(源码+lw+部署文档+讲解等)
29 2
|
8天前
|
JavaScript Java 测试技术
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
27 1