动态内存管理
根据需要分配内存和回收内存
- 通常在一块较大且连续的内存空间上进行分配和回收
动态内存管理解决的问题
- 内存资源稀缺,通过内存复用增加任务的并发性
动态内存管理的本质
- 时间换空间,通过动态分配和回收 "扩大物理内存"
动态内存管理的关键
时间效率
- 从发出内存申请到获得内存的时间越短越好
空间效率
- 为管理内存而占有的内存越少越好
碎片化
- 最大可分配内存占空闲内存总和的比例越大越好
动态内存管理的分配
定长内存管理
- 将内存分为大小相同的单元,每次申请一个单元的内存
变长内存管理
- 每次申请需要的内存 (大小不固定,以字节为单位)
定长内存管理的设计与实现
将内存分为两部分:管理单元 & 分配单元
管理单元与分配单元一一对应
将管理单元组织成链表 (定长内存管理链表)
管理内存时,操作链表头
- 申请内存
从链表头获取一个管理单元,并计算下标
根据下标计算分配单元的内存起始位置
- 归还内存
计算分配单元的下标,并将对应下标的管理单元插入链表
关键数据类型
定长内存管理
utility.h
#define AddrIndex(b, a) (((uint)(b) - (uint)(a)) / sizeof(*(a)))
memory.h
#ifndef MEMORY_H
#define MEMORY_H
#include "type.h"
void fmem_test();
#endif
memory.c
#include "memory.h"
#include "utility.h"
#include <stdio.h>
#include <time.h>
#define FM_ALLOC_SIZE 32
#define FM_NODE_SIZE sizeof(FMemNode)
typedef union _FMemNode FMemNode;
typedef byte (FMemUnit)[FM_ALLOC_SIZE];
union _FMemNode
{
FMemNode* next;
FMemUnit* ptr;
};
typedef struct
{
FMemNode* node;
FMemNode* nbase;
FMemUnit* ubase;
uint max;
} FMemList;
static FMemList gFMemList = {0};
static void FMemInit(byte* mem, uint size)
{
uint max = size / (FM_ALLOC_SIZE + FM_NODE_SIZE);
printf("max = %d\n", max);
uint i = 0;
FMemNode* p = NULL;
gFMemList.max = max;
gFMemList.nbase = (FMemNode*)mem;
gFMemList.ubase = (FMemUnit*)((uint)mem + max * FM_NODE_SIZE);
gFMemList.node = (FMemNode*)mem;
p = gFMemList.node;
for(i = 0; i < max - 1; i++)
{
FMemNode* current = AddrOff(p, i);
FMemNode* next = AddrOff(p, i + 1);
current->next = next;
}
((FMemNode*)AddrOff(p, i))->next = NULL;
}
static void* FMemAlloc()
{
void* ret = NULL;
FMemNode* alloc = gFMemList.node;
if(alloc)
{
int index = AddrIndex(alloc, gFMemList.nbase);
ret = (FMemUnit*)AddrOff(gFMemList.ubase, index);
gFMemList.node = alloc->next;
alloc->ptr = ret;
}
return ret;
}
static int FMemFree(void* ptr)
{
int ret = 0;
if(ptr)
{
uint index = AddrIndex((FMemUnit*)ptr, gFMemList.ubase);
FMemNode* node = (FMemNode*)AddrOff(gFMemList.nbase, index);
if((index < gFMemList.max) && IsEqual(node->ptr, ptr))
{
node->next = gFMemList.node;
gFMemList.node = node;
ret = 1;
}
}
return ret;
}
void fmem_test()
{
static byte fmem[0x10000] = {0};
static void* array[2000] = {0};
FMemNode* pos = NULL;
int i = 0;
FMemInit(fmem, sizeof(fmem));
pos = gFMemList.node;
while(pos)
{
i++;
pos = pos->next;
}
printf("i = %d\n", i);
for(i = 0; i < 10000; i++)
{
int ii = i % 2000;
void* p = FMemAlloc();
if(array[ii])
{
FMemFree(array[ii]);
array[ii] = NULL;
}
array[ii] = p;
if(i % 3 == 0)
{
int index = rand() % 2000;
FMemFree(array[index]);
array[index] = NULL;
}
}
for(i = 0; i < 2000; i++)
{
FMemFree(array[i]);
}
i = 0;
pos = gFMemList.node;
while(pos)
{
i++;
pos = pos->next;
}
printf("i = %d\n", i);
}
test.c
#include "memory.h"
int main()
{
fmem_test();
return 0;
}
FMemNode 定义为链表结点,数据类型定义为 union 是为了减少内存的损耗;FMemList 定义为链表的头结点,nbase 为管理单元的内存起始地址,ubase 为分配单元的内存起始地址,max 为最大可分配的内存单元数。
FMemUnit 的类型为 FM_ALLOC_SIZE 个 byte 大小的数组类型,我们定义一次分配的内存大小为32个字节。
FMemInit 函数从 mem 中构造管理单元和分配单元,将管理单元组织称为链表。
FMemAlloc 函数用于分配一个内存单元,首先先计算出链表头结点对应管理单元的下标,然后根据下标计算分配单元的起始地址,将这个起始地址作为分配出去内存单元的起始地址,gFMemList.node 赋值为下一个结点,最后将 alloc->ptr 设置为分配出去内存单元的起始地址,这个将作为内存回收的标志。
FMemFree 函数用于回收一个内存单元,首先计算出要回收的内存单元在分配单元的下标,根据下标计算对应的管理单元,判断对应管理单元的 ptr 与要回收的 ptr 是否相等,相等并且 index 合法的话,就将这个管理单元结点插入到链表的头部,以便下次申请内存时使用。
fmem_test 函数用来测试我们编写的定长动态内存分配的相关函数是否编写正确,fmem 作为动态内存分配出去,初始化内存管理,打印管理单元结点的值 i;在经过一系列的分配内存和回收内存后,再打印管理结点 i 的值,如果都相等,并且 i = len / (FM_ALLOC_SIZE + FM_NODE_SIZE),则证明我们编写的定长动态内存管理的相关函数是正确的。
两次 i 的值都相等,i = 0x10000 / (32 + 4) = 1820,i 的值也是正确的,我们编写的定长动态内存管理相关函数是正确的!