Java伙伴系统(模拟)

参考:https://labrick.cc/2015/10/12/buddy-system-algorithm/

 代码过烂 不宜参考。

output:

[operating.entity.Heap@4554617c, 1048576]
**************begin mallocing memory*****************
heap.myMalloc(16), 分割 16 次
[operating.entity.Heap@4554617c, 1048560]
heap.myMalloc(32), 分割 0 次
[operating.entity.Heap@4554617c, 1048528]
heap.myMalloc(48), 分割 0 次
[operating.entity.Heap@4554617c, 1048464]
heap.myMalloc(64), 分割 1 次
[operating.entity.Heap@4554617c, 1048400]
heap.myMalloc(80), 分割 1 次
[operating.entity.Heap@4554617c, 1048272]
**************begin freeing memory*****************
heap.myFree(32), 合并0次
[operating.entity.Heap@4554617c, 1048304]
heap.myFree(128), 合并1次
[operating.entity.Heap@4554617c, 1048368]
heap.myFree(0), 合并2次
[operating.entity.Heap@4554617c, 1048384]
heap.myFree(64), 合并2次
[operating.entity.Heap@4554617c, 1048448]
heap.myFree(256), 合并13次
[operating.entity.Heap@4554617c, 1048576]

code:

package operating.test;

import operating.entity.Heap;

public class HeapTest {

    public static void main(String[] args) {
        Heap heap = new Heap();
        heap.printList();

        System.out.println("**************begin mallocing memory*****************");
        int ptr16 = heap.myMalloc(16);
        int ptr32 = heap.myMalloc(32);
        int ptr48 = heap.myMalloc(48);
        int ptr64 = heap.myMalloc(64);
        int ptr80 = heap.myMalloc(80);

        System.out.println("**************begin freeing memory*****************");
        heap.myFree(ptr32);
        heap.myFree(ptr64);
        heap.myFree(ptr16);
        heap.myFree(ptr48);
        heap.myFree(ptr80);
    }
}

/

package operating.entity;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;

public class Heap {
    /**
     * 堆空间heap初始大小
     */
    private static final int HEAP_SIZE = 1024*1024;
    /**
     * 空闲块切割后若剩余不超过RESIDUE,则不进行切割
     */
    private static final int RESIDUE = 8;
    /**
     * 用一个int数组来模拟堆
     */
    private int[] memory;
    /**
     * 用于管理内存的分配状态,采用伙伴系统
     */
    private HashMap<Integer, LinkedList<Integer>> blockManager = new HashMap<>();

    public Heap() {
        memory = new int[HEAP_SIZE];
        Arrays.fill(memory, 0);

        LinkedList<Integer> initBlock = new LinkedList<>(); // 创建可存放最大块 1024*1024 的链表
        initBlock.add(0); // 添加一个可用的块,起始地址为 0
        blockManager.put(HEAP_SIZE, initBlock); // 将链表添加到映射中 (1024*1024,链表(只含有一个块))
    }

    /**
     * 计算块大小 2^i,使得 2^(i-1) < n <= 2^i
     * @param requestSize
     * @return
     */
    private int getBlockSize(int requestSize) {
        if (requestSize <= RESIDUE) return RESIDUE; // 如果所请求的块小于最小可分割块则直接返回最小可分割块大小

        int i = 4;
        while (requestSize > Math.pow(2, i)) {
            ++i;
        }
        return (int) Math.pow(2, i);
    }

    /**
     * 查找可用的块
     * @param blockSize
     * @return
     */
    private int searchAvailable(int blockSize) {
        LinkedList<Integer> blocks = blockManager.get(blockSize);
        if (blocks != null) { // 如果恰好有该大小的内存块
            for (Integer x : blocks) {
                if (memory[x] != 1) { // 并且还没被使用
                    return x;
                }
            }
        }
        return -1;
    }

    /**
     * 分割块: 2^i 转变为两个 2^(i-1)
     * @param address
     * @param size
     */
    private void parting(Integer address, int size) {
        LinkedList<Integer> bigBlocks = blockManager.get(size); // 取得 size 大小的块
        bigBlocks.remove(address);
        LinkedList<Integer> smallBlocks = blockManager.get(size/2);
        if (smallBlocks == null) {
            smallBlocks = new LinkedList<>();
            blockManager.put(size/2, smallBlocks);
        }
        smallBlocks.add(address);
        smallBlocks.add(address + size/2);
    }

    /**
     * 合并
     * @param address
     * @param buddyAddress
     * @param size
     */
    private void merge(Integer address, Integer buddyAddress, int size) {
        LinkedList<Integer> smallBlocks = blockManager.get(size);
        if (smallBlocks == null) return;
        smallBlocks.remove(address);
        smallBlocks.remove(buddyAddress);
        LinkedList<Integer> bigBlocks = blockManager.get(size*2);
        bigBlocks.add(address < buddyAddress ? address : buddyAddress);
    }

    /**
     * 通过地址得到相应的块大小
     * @param address
     * @return
     */
    private int getSize(int address) {
        for (Integer size : blockManager.keySet()) {
            LinkedList<Integer> blocks = blockManager.get(size);
            for (Integer x : blocks) {
                if (x == address) return size;
            }
        }
        return 0;
    }

    /**
     * 分配内存
     * @param size 请求的内存大小
     * @return 分配内存的起始地址
     */
    public int myMalloc(int size) {
        int count = 0; // 计算分割次数
        // 计算所需要的块的大小
        int requestSize = getBlockSize(size);
        // 1- 如果恰好有该大小的块,直接分配并返回
        int address = searchAvailable(requestSize);
        if (address != -1) {
            memory[address] = 1;
            System.out.println("heap.myMalloc("+ size + ")," + " 分割 " + count + " 次");
            this.printList();
            return address;
        }

        // 2- 如果没有就分割,逐级向上找可以分割的块
        int tempSize = requestSize;
        while (address == -1 && tempSize <= HEAP_SIZE) {
            // System.out.println("正在搜索 " + tempSize + "大小的块。");
            address = searchAvailable(tempSize*=2);
        }
        // System.out.println("找到了可分割的块。");
        if (tempSize > HEAP_SIZE) {
            System.out.println("没有足够的空间!");
            return -1;
        } else { // 分割出需要的块
            while (searchAvailable(requestSize) == -1) {
                // System.out.println("正在对起始地址为" + address + "大小为" + tempSize + "的块进行分割");
                parting(address, tempSize);
                ++ count;
                tempSize = tempSize/2;
            }
        }

        // 3- 重复 1
        address = searchAvailable(requestSize);
        memory[address] = 1;
        System.out.println("heap.myMalloc("+ size + ")," + " 分割 " + count + " 次");
        this.printList();
        return address;
    }

    /**
     * 释放起始地址为 address 的内存
     * @param address
     */
    public void myFree(int address) {
        int count = 0; // 计算合并次数
        int originAddress = address;
        memory[address] = 0;
        while (true) {
            int size = getSize(address);
            // 计算伙伴块的地址
            int buddyAddress = -1;
            if (size != 0 && address % (size*2) == size) {
                buddyAddress = address - size;
            } else {
                buddyAddress = address + size;
            }
            if (buddyAddress >=0 && buddyAddress < HEAP_SIZE && memory[buddyAddress] != 1) { // 如果伙伴块没被使用就合并
                merge(address, buddyAddress, size);
                ++count;
            } else {
                break;
            }
            if (buddyAddress < address) {
                int temp = address;
                address = buddyAddress;
                buddyAddress = temp;
            }
        }
        System.out.println("heap.myFree("+ originAddress + ")," + " 合并" + count + "次");
        this.printList();
    }

    public void printList() {
        int rest = HEAP_SIZE;
        for (Integer size : blockManager.keySet()) {
           LinkedList<Integer> blocks = blockManager.get(size);
           for (Integer x : blocks) {
                if (memory[x] == 1) {
                    rest -= size;
                }
            }
        }
        // 仅仅是模拟,java 无法真正获取对象内存地址
        System.out.println("[" + this + ", " + rest + "]");
    }
}

 

转载于:https://www.cnblogs.com/xkxf/p/8137287.html

假设系统的可利用空间容量为2m个字,则系统开始运行时,整个内存区是一个大小为2m的空闲分区。在系统运行过程中,由于不断的划分,可能会形成若干个不连续的空闲分区,将这些空闲分区根据分区的大小进行分类,对于每一类具有相同大小的所有空闲分区,单独设立一个空闲分区双向链表。这样,不同大小的空闲分区形成了k(0≤k≤m)个空闲分区链表。 当需要为进程分配一个长度为n的存储空间时,首先计算一个i值,使2i-1<n≤2i,然后在空闲分区大小为2i的空闲分区链表中查找。若找到,即把该空闲分区分配给进程。否则,表明长度为2i的空闲分区已经耗尽,则在分区大小为2i+1的空闲分区链表中寻找。若存在2i+1的一个空闲分区,则把该空闲分区分为相等的连个分区,这两个分区称为一对伙伴,其中的一个分区用于分配,而把另一个加入分区大小为2i的空闲分区链表中。若大小为2i+1的空闲分区不存在,则需要查找大小为2i+2的空闲分区,若找到则对其进行两次分割:第一次,将其分割为大小为2i+1的两个分区,一个用于分配,一个加入到大小为2i+1空闲分区链表中;第二次,将第一次用于分配的空闲分区分割为2i的两个分区,一个用于分配,一个加入到大小为2i空闲分区链表中。若仍然找不到,则继续查找大小为2i+3的空闲分区,以此类推。由此可见,在最坏的情况下,可能需要对2k的空闲分区进行k次分割才能得到所需分区。 与一次分配可能要进行多次分割一样,一次回收也可能要进行多次合并,如回收大小为2i的空闲分区时,若事先已存在2i的空闲分区时,则应将其与伙伴分区合并为大小为2i+1的空闲分区,若事先已存在2i+1的空闲分区时,又应继续与其伙伴分区合并为大小为2i+2的空闲分区,依此类推。 2.2 伙伴系统的需求 根据伙伴系统算法的思想,我们组对本系统的功能划分为3种: ⑴ 根据伙伴系统算法分配内存 ⑵ 根据伙伴系统算法回收内存 ⑶ 实时查看内存使用的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值