本文讲述一种的内存动态管理机制:边界标识法。关于边界标识法大家有兴趣的话可以阅读清华计算机教材《数据结构》(作者:严蔚敏、吴伟民)第八章(动态存储管理)中第3节(边界标识法)。本文主要是针对从事后台服务器程序领域讲述这种内存分配机制的应用场景和优缺点。
本文提出的内存分配管理策略是对清华计算机教材《数据结构》(作者:严蔚敏、吴伟民)第八章(动态存储管理)中第3节(边界标识法)示例代码的整理和封装。
3. 工作机制
应用程序首先调用mallo()函数预先分配出一块内存空间,然后在这个内存空间中使用边界标识法分配出应用程序需要的内存,内存释放时,会自动合并前后紧邻的空闲块,形成更大的空闲块。
4. 分配机制特点
该机制优点是:
- 可以根据需要分配任意大小的空间,不需要取整。
- 释放内存操作极快。
该机制缺点是:
1、长时间运行可能会产生大量碎片,导致内存分配的效率降低和内存的浪费。
针对服务器程序应用场景:
1、需要连续内存空间。
2、分配内存大小范围大,从1个字节到上兆、几十兆。
3、分配的空间只是临时使用,用完就释放,不至于产生大量碎片。
另外:如果设计的软件对性能要求不高,也可以考虑使用此种内存分配机制。
5. 源码文件
5.1 MemAlloc.h
#ifndef MEMALLOC_H
#define MEMALLOC_H
typedef void* HANDLE;
HANDLE CreateHeap(int nHeapSize);
void DestroyHeap(HANDLE hHeap);
void* AllocMem(HANDLE hHeap, int nSize);
void FreeMem(HANDLE hHeap, void* pAddr);
int GetAddrSize(void* pAddr);
void FreeAllMem(HANDLE hHeap);
#endif
5.2 MemAlloc.cpp
#include "MemAlloc.h"
#include "TagShare.h"
#include <stdio.h>
static AllocTail* FootLoc(AllocNode* pHead)
{
return (AllocTail *)((char *)pHead + pHead->nSize - sizeof(AllocTail));
}
int GetSelfUplink(AllocNode* p)
{
return ((AllocTail *)((char *)p + p->nSize - sizeof(AllocTail)))->nUpLink;
}
static AllocNode* GetLeftNode(HeapInfo* pHeapInfo, AllocNode* p)
{
return (AllocNode*)(pHeapInfo->pBuf + ((AllocTail *)((char *)p - sizeof(AllocTail)))->nUpLink);
}
HANDLE CreateHeap(int nHeapSize)
{
HeapInfo* pHeapInfo;
pHeapInfo = (HeapInfo*)(new char[sizeof(HeapInfo) + nHeapSize + SHELL_SIZE]);
if(pHeapInfo == NULL)
{
return NULL;
}
pHeapInfo->nHeapSize = nHeapSize + SHELL_SIZE;
pHeapInfo->nCount = 1;
pHeapInfo->pHead = (AllocNode *)pHeapInfo->pBuf;
pHeapInfo->pTail = (AllocNode *)pHeapInfo->pBuf;
AllocNode* pNode;
pNode = pHeapInfo->pHead;
pNode->nLeftLink = -1;
pNode->nTag = 0;
pNode->nSize = pHeapInfo->nHeapSize;
pNode->nRightLink = -1;
AllocTail* pTail;
pTail = FootLoc(pNode);
pTail->nUpLink = 0;
pTail->nTag = 0;
return pHeapInfo;
}
void DestroyHeap(HANDLE hHeap)
{
HeapInfo* pHeapInfo;
pHeapInfo = (HeapInfo *)hHeap;
if(pHeapInfo != NULL)
{
delete[] (char *)pHeapInfo;
pHeapInfo = NULL;
}
return;
}
static void RemoveHead(HeapInfo* pHeapInfo)
{
pHeapInfo->nCount--;
if(pHeapInfo->pHead->nRightLink == -1)
{
pHeapInfo->pHead = NULL;
}
else
{
pHeapInfo->pHead = (AllocNode *)(pHeapInfo->pBuf + pHeapInfo->pHead->nRightLink);
}
if(pHeapInfo->pHead != NULL)
{
pHeapInfo->pHead->nLeftLink = -1;
if(pHeapInfo->nCount == 1)
{
pHeapInfo->pHead->nRightLink = -1;
}
}
else
{
pHeapInfo->pTail = NULL;
}
return;
}
static void RemoveTail(HeapInfo* pHeapInfo)
{
pHeapInfo->nCount--;
if(pHeapInfo->pTail->nLeftLink == -1)
{
pHeapInfo->pTail = NULL;
}
else
{
pHeapInfo->pTail = (AllocNode *)(pHeapInfo->pBuf + pHeapInfo->pTail->nLeftLink);
}
if(pHeapInfo->pTail != NULL)
{
pHeapInfo->pTail->nRightLink = -1;
if(pHeapInfo->nCount == 1)
{
pHeapInfo->pHead->nLeftLink = -1;
}
}
else
{
pHeapInfo->pHead = NULL;
}
return;
}
static void RemoveAt(HeapInfo* pHeapInfo, AllocNode* pNode)
{
if(pNode == pHeapInfo->pHead)
{
RemoveHead(pHeapInfo);
return;
}
if(pNode == pHeapInfo->pTail)
{
RemoveTail(pHeapInfo);
return;
}
AllocNode* pPrev;
AllocNode* pNext;
if(pNode->nLeftLink == -1)
{
pPrev = NULL;
}
else
{
pPrev = (AllocNode *)(pHeapInfo->pBuf + pNode->nLeftLink);
}
if(pNode->nRightLink == -1)
{
pNext = NULL;
}
else
{
pNext = (AllocNode *)(pHeapInfo->pBuf + pNode->nRightLink);
}
if(pNext == NULL)
{
pPrev->nRightLink = -1;
}
else
{
if(pPrev != NULL)
{
pPrev->nRightLink = GetSelfUplink(pNext);
}
}
if(pPrev == NULL)
{
pNext->nLeftLink = -1;
}
else
{
if(pNext != NULL)
{
pNext->nLeftLink = GetSelfUplink(pPrev);
}
}
pHeapInfo->nCount--;
return;
}
void* AllocMem(HANDLE hHeap, int nSize)
{
HeapInfo* pHeapInfo;
pHeapInfo = (HeapInfo *)hHeap;
if(pHeapInfo->nCount == 0)
{
return NULL;
}
nSize += SHELL_SIZE;
AllocNode* p;
p = pHeapInfo->pHead;
while(true)
{
if(p->nSize < nSize)
{ // 不符合条件
if(p->nRightLink < 0)
{ // 循环链表构成循环
return NULL;
}
p = (AllocNode *)(pHeapInfo->pBuf + p->nRightLink);
}
else
{ // 满足条件
if(p->nSize - nSize <= MINI)
{ // 把整个空闲块进行整体分配
AllocTail* pFoot;
pFoot = FootLoc(p); // 取尾巴结构
p->nExtraSize = p->nSize - nSize;
p->nTag = 1; // 设置头占用标志
pFoot->nTag = 1; // 设置尾占用标志
RemoveAt(pHeapInfo, p);
return p+1;
}
else
{
AllocTail* pOldFreeFoot;
pOldFreeFoot = FootLoc(p); // 取尾巴结构
pOldFreeFoot->nTag = 1; // 设置占用块尾巴标记(tag):1(占用)
p->nSize -= nSize; // 设置新空闲块头size:
AllocTail* pNewFreeFoot;
pNewFreeFoot = FootLoc(p); // 获取新空闲块的foot
pNewFreeFoot->nTag = 0; // 设置新空闲块尾巴标记(tag):0(空闲)
pNewFreeFoot->nUpLink = pOldFreeFoot->nUpLink; // 设置新空闲块的uplink
pOldFreeFoot->nUpLink = pNewFreeFoot->nUpLink + p->nSize;
AllocNode* pBusyHead;
pBusyHead = (AllocNode*)((char *)pNewFreeFoot + sizeof(AllocTail));
pBusyHead->nExtraSize = 0;
pBusyHead->nTag = 1; // p为占用块的头的标记
pBusyHead->nSize = nSize; // p为占用块的头的的size
pBusyHead++;
return pBusyHead;
}
}
}
}
// 左:忙 右:忙
static void FreeMem1(HeapInfo* pHeapInfo, AllocNode* p)
{
p->nTag = 0;
AllocTail* pFoot;
pFoot = FootLoc(p);
pFoot->nTag = 0;
if(pHeapInfo->nCount == 0)
{
p->nRightLink = -1;
p->nLeftLink = -1;
pHeapInfo->pHead = p;
pHeapInfo->pTail = p;
pHeapInfo->nCount = 1;
}
else
{
pHeapInfo->pTail->nRightLink = GetSelfUplink(p);
p->nLeftLink = GetSelfUplink(pHeapInfo->pTail);
p->nRightLink = -1;
pHeapInfo->pTail = p;
pHeapInfo->nCount++;
}
}
// 左:闲 右:忙
static void FreeMem2(HeapInfo* pHeapInfo, AllocNode* p)
{
int n;
n = p->nSize;
AllocNode* pLeftNode;
pLeftNode = GetLeftNode(pHeapInfo, p);
int nUplink;
nUplink = GetSelfUplink(pLeftNode);
pLeftNode->nSize += n;
AllocTail* pFoot;
pFoot = FootLoc(p);
pFoot->nUpLink = nUplink;
pFoot->nTag = 0;
return;
}
// 左:忙 右:闲
static void FreeMem3(HeapInfo* pHeapInfo, AllocNode* p)
{
AllocNode* pRightNode; // 右邻空闲区的头
pRightNode = (AllocNode *)((char *)p + p->nSize); // 获取右邻空闲区的头
p->nTag = 0; // p为合并后的节点头部
int nUpLink;
nUpLink = GetSelfUplink(p);
int nRightFreeSize;
nRightFreeSize = pRightNode->nSize;
RemoveAt(pHeapInfo, pRightNode);
p->nSize += nRightFreeSize;
FootLoc(p)->nUpLink = nUpLink;
///增加一个空闲块//
if(pHeapInfo->nCount == 0)
{
p->nRightLink = -1;
p->nLeftLink = -1;
pHeapInfo->pHead = p;
pHeapInfo->pTail = p;
pHeapInfo->nCount = 1;
}
else
{
pHeapInfo->pTail->nRightLink = GetSelfUplink(p);
p->nLeftLink = GetSelfUplink(pHeapInfo->pTail);
p->nRightLink = -1;
pHeapInfo->pTail = p;
pHeapInfo->nCount++;
}
//
return;
}
// 左:闲 右:闲
static void FreeMem4(HeapInfo* pHeapInfo, AllocNode* p)
{
int n;
n = p->nSize;
AllocNode* pLeftNode; // 左空闲节点
pLeftNode = GetLeftNode(pHeapInfo, p);
int nUpLink;
nUpLink = GetSelfUplink(pLeftNode);
AllocNode* pRightNode; // 右空闲节点
pRightNode = (AllocNode *)((char *)p + p->nSize);
RemoveAt(pHeapInfo, pRightNode);
pLeftNode->nSize += n + pRightNode->nSize;
FootLoc(pLeftNode)->nUpLink = nUpLink;
return;
}
void FreeMem(HANDLE hHeap, void* pAddr)
{
HeapInfo* pHeapInfo;
pHeapInfo = (HeapInfo*)hHeap;
AllocNode* p;
p = (AllocNode *)pAddr;
p--;
if((char *)p == pHeapInfo->pBuf)
{ // 释放地址为堆的头
if(pHeapInfo->nCount == 0)
{ // 没有空闲
FreeMem1(pHeapInfo, p);
return ;
}
AllocNode* pRightHead; // 右邻空闲区的头
pRightHead = (AllocNode *)((char *)p + p->nSize); // 获取右邻空闲区的头
int nRightTag;
nRightTag = pRightHead->nTag;
int nLeftTag;
nLeftTag = 1;
if(nRightTag == 1)
{ // 右忙
FreeMem1(pHeapInfo, p);
return ;
}
else
{ // 右闲
FreeMem3(pHeapInfo, p);
return ;
}
}
char* pTail;
pTail = (char *)p + p->nSize;
if(pTail == pHeapInfo->pBuf + pHeapInfo->nHeapSize)
{
if(pHeapInfo->nCount == 0)
{ // 没有空闲
FreeMem1(pHeapInfo, p);
return ;
}
int nLeftTag;
nLeftTag = ((AllocTail *)((char*)p-sizeof(AllocTail)))->nTag;
int nRightTag;
nRightTag = 1;
if(nLeftTag == 1)
{ // 左忙
FreeMem1(pHeapInfo, p);
return ;
}
else
{ // 左闲
FreeMem2(pHeapInfo, p);
return ;
}
}
int nLeftTag;
nLeftTag = ((AllocTail *)((char*)p-sizeof(AllocTail)))->nTag;
AllocNode* pRightHead; // 右邻空闲区的头
pRightHead = (AllocNode *)((char *)p + p->nSize); // 获取右邻空闲区的头
int nRightTag;
nRightTag = pRightHead->nTag;
if (nLeftTag == 1 && nRightTag == 1)
{
FreeMem1(pHeapInfo, p);
return ;
}
if (nLeftTag == 0 && nRightTag == 1)
{
FreeMem2(pHeapInfo, p);
return ;
}
if (nLeftTag == 1 && nRightTag == 0)
{
FreeMem3(pHeapInfo, p);
return ;
}
if (nLeftTag == 0 && nRightTag == 0)
{
FreeMem4(pHeapInfo, p);
return ;
}
printf("error");
return ;
}
int GetAddrSize(void* pAddr)
{
AllocNode* p;
p = (AllocNode *)pAddr;
p--;
return (p->nSize - sizeof(AllocNode) - sizeof(AllocTail) - p->nExtraSize);
}
void FreeAllMem(HANDLE hHeap)
{
HeapInfo* pHeapInfo;
pHeapInfo = (HeapInfo *)hHeap;
pHeapInfo->nCount = 1;
pHeapInfo->pHead = (AllocNode *)pHeapInfo->pBuf;
pHeapInfo->pTail = (AllocNode *)pHeapInfo->pBuf;
AllocNode* pNode;
pNode = pHeapInfo->pHead;
pNode->nLeftLink = -1;
pNode->nTag = 0;
pNode->nSize = pHeapInfo->nHeapSize;
pNode->nRightLink = -1;
AllocTail* pTail;
pTail = FootLoc(pNode);
pTail->nUpLink = 0;
pTail->nTag = 0;
return;
}
6. 数组接口函数使用说明
6.1 CreateHeap()
1、功能说明
此函数用来生成一个内存堆数据结构,执行成功后返回一个句柄,之后分配和释放内存的操作都引用此句柄。
2、函数原型
HANDLE CreateHeap(int nHeapSize);
3、参数说明
- int nHeapSize
输入参数。堆空间的大小, 单位字节, nHeapSize必须大于0。
4、返回值
执行成功返回非NULL的句柄,执行失败时返回NULL。
5、相关函数
DestroyHeap(HANDLE hHeap)
6.2 DestroyHeap ()
1、功能说明
此函数用来释放指定的堆数据结构以及占用的内存。
2、函数原型
void DestroyHeap(HANDLE hHeap);
3、参数说明
1)HANDLE hHeap
输入参数,hHeap是执行CreateHeap函数返回的句柄。
4、返回值
无。
5、相关函数
CreateHugeArray()
6.3 AllocMem()
1、功能说明
此函数用来根据数组索引下标获指定取数组元素地址。
2、函数原型
Void* AllocHeap(HANDLE hHeap, int nSize);
3、参数说明
1)HANDLE hHeap
输入参数,hHeap是执行CreateHeap函数返回的句柄。
2)int nSize
输入参数,申请内存的大小,单位:字节。
4、返回值
地址指针
5、相关函数
FreeMem()
6.4 FreeMem()
1、功能说明
此函数用来根据数组索引下标获指定取数组元素地址。
2、函数原型
Void FreeMem(HANDLE hHeap,void* pAddr);
3、参数说明
1)HANDLE hHeap
输入参数,hHeap是执行CreateHeap函数返回的句柄。
2)void* pAddr
输入参数,要释放内存的地址,这个地址必须是之前通过AllocMem函数返回的
地址。
4、返回值
无
5、相关函数
AllocMem()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MemAlloc.h"
int main(int argc, char* argv[])
{
HANDLE hHeap;
hHeap = CreateHeap(10*1024*1024);
void* pAddrArray[1024];
int i;
for (i = 0; i < 1024; i++)
{
pAddrArray[i] = AllocMem(hHeap, 256);
}
for (i = 0; i < 1024; i++)
{
strcpy(pAddrArray[i], "1234567890");
}
for (i = 0; i < 1024; i++)
{
printf("%s\r\n", pAddrArray[i]);
}
for (i = 0; i < 1024; i++)
{
FreeMem(hHeap, pAddrArray[i]);
}
DestroyHeap(hHeap);
return 0;
}