内存管理有内存分配和内存释放两部分。
我们假设现在有128M内存可以使用,且将这些内存分块,每块为4KB,那么,共有32768个块。书中列举了两种方法来管理这些块。
1、用char table[32768]来保存内存的分配情况,如果第i块用了,则char[i] = '1',否则为'0'
class MEMBLOCKS
{
private:
static const unsigned int MAX_MEM_BLOCKS = 32768; //最大的内存块数
static const unsigned int PER_MEM_BLOCK = 4 * 1024; //每块的大小为4KB
static const unsigned int MEM_SUM = MAX_MEM_BLOCKS * PER_MEM_BLOCK; //总字节数,即总的可用内存
unsigned int addr; //初始化时申请到的内存的初始地址
char table[MAX_MEM_BLOCKS]; //记录可用块
int get_block_id(int addr); //根据addr得出块号
public:
MEMBLOCKS();
int allocate(int size); //分配size字节的内存并返回地址,分配失败返回0
int free(unsigned int addr,unsigned int size);//释放内存
};
MEMBLOCKS::MEMBLOCKS()
{
addr = 0x00ffff;
for(int i = 0;i < MAX_MEM_BLOCKS;i++)
table[i] = '0';
}
int MEMBLOCKS::allocate(int size)
{
if(size <= 0)
return 0;
int sum,i;
sum = i = 0;
while(1)
{
int tmp = i;
sum = 0;
while(i < MAX_MEM_BLOCKS && table[i] == '0' && sum < size)
{
sum += PER_MEM_BLOCK;
i++;
}
if(sum >= size)
{
int addr = this->addr + tmp * PER_MEM_BLOCK;
while(tmp <= i)
table[tmp++] = '1';
return addr;
}
if(i >= MAX_MEM_BLOCKS)
return 0;
i++;
}
return 0;
}
int MEMBLOCKS::free(unsigned int addr,unsigned int size)
{
int lid = get_block_id(addr);
int rid = get_block_id(addr + size - 1);
if(lid < 0)
return -1;
while(lid <= rid && lid < MAX_MEM_BLOCKS)
table[lid++] = '0';
return 1;
}
int MEMBLOCKS::get_block_id(int addr)
{
return (addr - this->addr) / PER_MEM_BLOCK;
}
这种分配方式是以块为单位来分配的,每次找到连续的块,它们的内存数量和大于用户需要的,则分配,且将相应的table[i]改成'1';如果遍历table都找不到,则分配失败;而在释放的时候,每次都是将释放的内存的块改成'0'。
我们可以计算出,这种方式,table的占用内存为32KB。在table的空间优化上,我们还可以用另一种方式:用bit位来表示内存的分配情况,那么char为1个字节,即8位,就可以表示8个块的内存使用情况,这样的话,我们只要用4096个char就可以了,那么,它占用的内存为4KB。不过这种方式在实现的时候有点难度,还没想到好办法。
2、用数组来表示。
如下面代码,定义了struct FREEMEM,它有两个属性,addr和size,分别表示该可用内存块的起始地址和大小。每次在分配的时候,如果需要分配的字节数小于等于能找到的最大可用块,那么就找一个这种可用块分配给它;而如果找不到的话,就需要将各个块连在一起,看连续的块(地址也要连续)能否满足分配的要求,如果不能就分配失败。在释放的时候,先将块按这个块应该所在的块号拆分(如果有的话),加到freemems数组中去,然后按地址进行排序,最后合并块,即地址连续有着同一个块号的freemems合并成一个。
这种方法分配的时候,并不是按4KB来分配的,而是按需分配,要多少,给多少,不会多给1字节。但这就引起了内存的碎片化非常严重,碎片太多可能会导致freemems不够用,我的程序还没有进行这样的处理。且分配的时候,可能分配的内存不在同一个块中,则需要将这个分配的内存拆分,分别释放。
class MEMBLOCKS
{
private:
static const unsigned int MAX_MEM_BLOCKS = 32768; //最大的内存块数
static const unsigned int PER_MEM_BLOCK = 4 * 1024; //每块的大小为4KB
static const unsigned int MEM_SUM = MAX_MEM_BLOCKS * PER_MEM_BLOCK; //总字节数,即初始化时要申请的内存字节数
unsigned short num; //块总数
unsigned int min_block; //最小块的字节数
unsigned int max_block; //最大块的字节数
unsigned int addr; //初始化时申请到的内存的初始地址
struct FREEMEM
{
/**
*addr: 可用内存的起始地址
*size: 从该起始地址起有size字节的内存可用
**/
unsigned int addr,size;
}freemems[MAX_MEM_BLOCKS];
int get_block_id(int addr); //得到块号,这里块号的定义是(addr - this->addr) / PER_MEM_BLOCK,即初始时freemems[num]中的i
int update_max_block(); //更新最大块max_block
void move_blocks(int start,int n); //移动块,即freemems[start + x] = freemems[start + n + x]
public:
MEMBLOCKS();
int allocate(int size); //分配size字节的内存并返回地址,分配失败返回0
int free(unsigned int addr,unsigned int size);//释放内存
void show_blocks(); //输出当前块的情况
~MEMBLOCKS();
};
MEMBLOCKS::MEMBLOCKS()
{
//addr = new int[MEM_SUM / 1024];
addr = 0x00ffff;
if(!addr)
{
cout << "Initialization failed!" << endl;
return;
}
num = MAX_MEM_BLOCKS;
max_block = min_block = PER_MEM_BLOCK * 1024;
for(int i = 0;i < num;i++)
{
freemems[i].addr = addr + PER_MEM_BLOCK * i;
freemems[i].size = PER_MEM_BLOCK;
}
show_blocks();
}
int MEMBLOCKS::allocate(int size)
{
if(size <= 0 || !num)
return 0;
//更新最大块
update_max_block();
if(size > max_block)
{
//申请的内存比现存的最大块还大时,看能否合并块,能的话就分配,不能就分配失败
for(int i = 0;i < num;)
{
int sum,j;
sum = freemems[i].size,j = i + 1;
while(j < num && freemems[j - 1].addr + freemems[j - 1].size == freemems[j].addr)
{
sum += freemems[j].size;
if(sum >= size)
{
//最后一块一部分分配出去
if(sum - size > 0)
{
freemems[j].addr = freemems[j].addr + freemems[j].size - (sum - size);
freemems[j].size = sum - size;
j--;
}
break;
}
j++;
}
if(sum >= size)//合并
{
//cout << "i: " << i << endl;
//cout << "j: " << j << endl;
int m = freemems[i].addr;
int tmp = j - i + 1;
move_blocks(i,tmp);
show_blocks();
return m;
}
//没找到合并点
i = j + 1;
}
return 0;
}
else
{
for(int i = 0;i < num;i++)
if(freemems[i].size >= size)
{
int tmp = freemems[i].addr;
freemems[i].addr += size;
freemems[i].size -= size;
if(!freemems[i].size)
move_blocks(i,1);
show_blocks();
return tmp;
}
return 0;
}
return 0;
}
int MEMBLOCKS::free(unsigned int addr,unsigned int size)
{
int laddr,raddr;
while(size > 0)
{
laddr = get_block_id(addr);
raddr = get_block_id(addr + size - 1);
//cout << "laddr: " << laddr << endl;
//cout << "raddr: " << raddr << endl;
if(laddr == raddr)
{
freemems[num++] = {addr,size};
size = 0;
}
else
{
int tmp = (laddr + 1) * PER_MEM_BLOCK + this->addr - addr;
size -= tmp;
freemems[num++] = {addr,tmp};
addr += tmp;
//cout << "addr: " << addr << endl;
}
}
//排序
for(int i = 0;i < num;i++)
for(int j = i + 1;j < num;j++)
if(freemems[i].addr > freemems[j].addr)
{
FREEMEM tmp = freemems[i];
freemems[i] = freemems[j];
freemems[j] = tmp;
}
//合并本应是一个块且内存连续的
for(int i = 1;i < num;i++)
if(get_block_id(freemems[i - 1].addr) == get_block_id(freemems[i].addr))
{
freemems[i - 1].size += freemems[i].size;
move_blocks(i,1);
i--; //保证下一次循环中i在当前位置不变
}
show_blocks();
return 1;
}
int MEMBLOCKS::update_max_block()
{
max_block = 0;
for(int i = 0;i < num;i++)
if(max_block < freemems[i].size)
max_block = freemems[i].size;
return max_block;
}
int MEMBLOCKS::get_block_id(int addr)
{
return (addr - this->addr) / PER_MEM_BLOCK;
}
void MEMBLOCKS::move_blocks(int start,int n)
{
num -= n;
for(int i = start;i < num;i++)
freemems[i] = freemems[i + n];
}
void MEMBLOCKS::show_blocks()
{
cout << "num of blocks: " << num << endl;
for(int i = 0;i < num;i++)
cout << i << ": " << "{" << freemems[i].addr << ", " << freemems[i].size << "}" << endl;
cout << endl;
}
MEMBLOCKS::~MEMBLOCKS()
{
//delete []addr;
}
int main()
{
int size,addr;
MEMBLOCKS mem;
while(cin >> addr >> size)
{
if(!addr)
{
int tmp = mem.allocate(size);
if(tmp)
cout << "allocate successfully! " << tmp << endl;
else
cout << "allocate failed!" << endl;
}
else
mem.free(addr,size);
}
return 0;
}