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 + "]");
    }
}

 

posted @ 2017-12-28 23:28  xkfx  阅读(464)  评论(0编辑  收藏  举报