// 申请内存块的类型
/* Memory block identification */
#define _FREE_BLOCK 0
#define _NORMAL_BLOCK 1
#define _CRT_BLOCK 2
#define _IGNORE_BLOCK 3
#define _CLIENT_BLOCK 4
#define _MAX_BLOCKS 5
#define nNoMansLandSize 4
// 管理内存块的数据结构
typedef struct _CrtMemBlockHeader
{
struct _CrtMemBlockHeader * pBlockHeaderNext;
struct _CrtMemBlockHeader * pBlockHeaderPrev;
char * szFileName;
int nLine;
size_t nDataSize;
int nBlockUse;
long lRequest;
unsigned char gap[nNoMansLandSize];
} _CrtMemBlockHeader;
最终内存块的布局图:
// 解析到有效数据的结构
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
// 从有效数据出找到实际申请空间的起始位置
#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)
/*
* Statics governing how often _CrtCheckMemory is called.
*/
static unsigned check_frequency = _CRTDBG_CHECK_DEFAULT_DF >> 16;
static unsigned check_counter;
static long _lRequestCurr = 1; /* 第x次请求 */
extern "C" _CRTIMP long _crtBreakAlloc = -1L; /* Break on allocation by request number */
static size_t _lTotalAlloc; /* 所有申请的总字节数 */
static size_t _lCurAlloc; /* 当前申请的总字节数 */
static size_t _lMaxAlloc; /* 一次申请的最大字节数 */
// 填充申请空间的字节信息
static unsigned char _bNoMansLandFill = 0xFD; /*有效数据部分前标记*/
static unsigned char _bAlignLandFill = 0xED; /*对齐字节数的标记 */
static unsigned char _bDeadLandFill = 0xDD; /*释放空间使用0xDD填充 */
static unsigned char _bCleanLandFill = 0xCD; /*申请有效数据空间使用0xCD填充 */
// 维护内存块的双向链表信息
static _CrtMemBlockHeader * _pFirstBlock; // 指向链表的头
static _CrtMemBlockHeader * _pLastBlock; // 指向链表的尾
// 申请内存和内存块管理的函数
extern "C" static void * __cdecl _heap_alloc_dbg_impl(
size_t nSize,
int nBlockUse,
const char * szFileName,
int nLine,
int * errno_tmp
)
{
long lRequest;
size_t blockSize;
int fIgnore = FALSE;
_CrtMemBlockHeader * pHead;
void *retval=NULL;
/* lock the heap
*/
_mlock(_HEAP_LOCK);
__try {
/* verify heap before allocation */
if (check_frequency > 0)
if (check_counter == (check_frequency - 1))
{
_ASSERTE(_CrtCheckMemory());
check_counter = 0;
}
else
check_counter++;
lRequest = _lRequestCurr;
/* break into debugger at specific memory allocation */
if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
_CrtDbgBreak();
/* forced failure */
if ((_pfnAllocHook) && !(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, (const unsigned char *)szFileName, nLine))
{
if (szFileName)
_RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n",
szFileName, nLine);
else
_RPT0(_CRT_WARN, "Client hook allocation failure.\n");
}
else
{
/* cannot ignore CRT allocations */
if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&
!(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))
fIgnore = TRUE;
/* Diagnostic memory allocation from this point on */
//检测申请空间是否超过所能申请的最大字节数
if (nSize > (size_t)(_HEAP_MAXREQ - nNoMansLandSize - sizeof(_CrtMemBlockHeader)))
{
_RPT1(_CRT_ERROR, "Invalid allocation size: %Iu bytes.\n", nSize);
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
}
else
{
//检测块的类型,用户程序均为NORMAL_BLOCK
if (!_BLOCK_TYPE_IS_VALID(nBlockUse))
{
_RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");
}
//计算所要实际申请内存块的总字节数
blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;
RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
//根据前面计算出的结果申请内存,实际是HeapAlloc函数申请
pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);
//检测是否申请成功,申请失败填充错误信息
if (pHead == NULL)
{
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
}
else
{
//记录本次申请成功
/* commit allocation */
++_lRequestCurr;
if (fIgnore)
{
pHead->pBlockHeaderNext = NULL;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = NULL;
pHead->nLine = IGNORE_LINE;
pHead->nDataSize = nSize;
pHead->nBlockUse = _IGNORE_BLOCK;
pHead->lRequest = IGNORE_REQ;
}
else {
//修改已申请字节的一些全局信息
/* keep track of total amount of memory allocated */
if (SIZE_MAX - _lTotalAlloc > nSize)
{
_lTotalAlloc += nSize;
}
else
{
_lTotalAlloc = SIZE_MAX;
}
_lCurAlloc += nSize;
if (_lCurAlloc > _lMaxAlloc)
_lMaxAlloc = _lCurAlloc;
//将新申请的结点插入到全局双向链表当中
if (_pFirstBlock)
_pFirstBlock->pBlockHeaderPrev = pHead;
else
_pLastBlock = pHead;
//对结构体成员赋值
pHead->pBlockHeaderNext = _pFirstBlock;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = (char *)szFileName;
pHead->nLine = nLine;
pHead->nDataSize = nSize;
pHead->nBlockUse = nBlockUse;
pHead->lRequest = lRequest;
/* link blocks together */
_pFirstBlock = pHead;
}
//填充申请的空间
/* fill in gap before and after real block */
memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
/* fill data with silly value (but non-zero) */
memset((void *)pbData(pHead), _bCleanLandFill, nSize);
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
//获取申请空间中有效数据的存放地址
retval=(void *)pbData(pHead);
}
}
}
}
__finally {
/* unlock the heap
*/
//解锁堆
_munlock(_HEAP_LOCK);
}
// 返回申请存放有效数据的实际地址
return retval;
}
// 将申请的内存块插入到块管理链表的示意图
// 内存申请的操作步骤:
1、对堆进行加锁
2、在正式申请空间之前,对堆进行校验
3、检测申请内存块的类型
4、检测内存空间是否充足,不够设置错误信息,返回NULL,否则进行5
5、检测块的类型
6、计算本次所要申请的内存块的总字节数
7、按照计算的总字节数申请内存,底层真正向堆申请空间的是HeapAlloc函数
8、检测是否申请成功,如果申请失败设置错误信息,返回NULL,否则执行9
9、修改请求次数和目前申请的总字节数
10、将新申请的内存块的新节点头插到双向链表中
11、给该结点对应的结构体赋值
12、填充空间
13、获取申请内存块中存放有效数据的真正位置
14、对堆进行解锁
15、返回有效数据区域的地址