粗解“new”之来龙去脉(一)
来源:互联网 发布:手机telnet软件 编辑:程序博客网 时间:2024/05/27 12:22
使用C++的程序员都很熟悉使用new来分配一块内存,但是new函数具体是怎么工作的?可能很多人都没有花时间去研究,今天花了半天时间,查阅了相关的一些源代码,对它的来龙去脉有了一个初步的了解,写得不是很详细,一个是记录一下,另一个就是抛砖引玉,有兴趣的人可以深入研究。如果有不对的地方,也欢迎大家指教。
由于文章较长,分成四个部分。
文中列出了讲解所用到的源代码,仅供大家参考。
好,接下来就开始我们的“new”之旅:
分配一段内存首先当然是调用new函数去分配,我们就从new函数出发:
void * operator new( unsigned int cb ){ return _nh_malloc( cb, 1 );}
(注:也有别的版本的new,但对后面的分析没有影响)
可以看出,new只是简单地调用了_nh_malloc, 其中第一个参数cb是要分配的大小,第二个参数是有没有new handler,使用new时传入了1,表示是有new handler的,关于new handler后面还会提到,但它的作用和使用方法不在本文范围内,可以自行在网上查找。
void * __cdecl _nh_malloc ( size_t nSize, int nhFlag ){ return _nh_malloc_dbg(nSize, nhFlag, _NORMAL_BLOCK, NULL, 0);}
这个函数也只是调用了它的debug版_nh_malloc_dbg。接着往下看这个函数,这个函数主要是一个无限循环的for语句块,有三种返回的情况:
1 成功分配一块内存;
2 分配失败且没有new handler设置;
3 分配失败且调用_callnewh返回0。
从下面的代码我们也能知道,使用new去分配一块内存时,如果失败的话会调用new handler,然后接着去尝试分配内存,直到分配成功或_callnewh返回0。
void * __cdecl _nh_malloc_dbg ( size_t nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine ){ void * pvBlk; for (;;) { /* lock the heap */ _mlock(_HEAP_LOCK); /* do the allocation */ pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine); /* unlock the heap */ _munlock(_HEAP_LOCK); if (pvBlk || nhFlag == 0) return pvBlk; /* call installed new handler */ if (!_callnewh(nSize)) return NULL; /* new handler was successful -- try to allocate again */ }}
_callnewh函数源代码,就是去调用new handler函数, _pnhHeap是一个函数指针。
extern "C" int __cdecl _callnewh(size_t size){ { _PNH pnh = _pnhHeap; if ( (pnh == NULL) || ((*pnh)(size) == 0) ) return 0; } return 1;}
可以看出分配内存的事是在函数_heap_alloc_dbg做的,我们再看看这个函数。
#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
static unsigned char _bNoMansLandFill = 0xFD; void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFileName, int nLine ){ long lRequest; size_t blockSize; int fIgnore = FALSE; _CrtMemBlockHeader * pHead; /* verify heap before allocation */ if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF) _ASSERTE(_CrtCheckMemory()); lRequest = _lRequestCurr; /* break into debugger at specific memory allocation */ if (lRequest == _crtBreakAlloc) _CrtDbgBreak(); /* forced failure */ if (!(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, 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"); return NULL; } /* 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 || nSize + nNoMansLandSize + sizeof(_CrtMemBlockHeader) > (size_t)_HEAP_MAXREQ) { _RPT1(_CRT_ERROR, "Invalid allocation size: %u bytes.\n", nSize); return NULL; } if (!_BLOCK_TYPE_IS_VALID(nBlockUse)) { _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n"); } blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;#ifndef WINHEAP /* round requested size */ blockSize = _ROUND2(blockSize, _GRANULARITY);#endif /* WINHEAP */ pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize); if (pHead == NULL) return NULL; /* 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 */ _lTotalAlloc += nSize; _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); return (void *)pbData(pHead);}
抛开其它的一些辅助设置和操作,我们主要是关心在什么地方分配内存,返回的指针是pHead, 而pHead是调用了函数 _heap_alloc_base得到地址,然后设置一些参数,设置几位为FD,设置nsize个0,返回这个地址。可见是在_heap_alloc_base做的分配内存的事。如果有兴趣可以new一块内存,看看内存中的值为多少?
- 粗解“new”之来龙去脉(一)
- 粗解“new”之来龙去脉(二)
- 粗解“new”之来龙去脉(三)
- 粗解“new”之来龙去脉(四)
- KMP算法之来龙去脉
- 符号之来龙去脉
- Cocos2d-x学习笔记(一)HelloCpp的来龙去脉
- 编码之痛(上)编码的来龙去脉
- MFC来龙去脉(侯捷)
- 【原创】MFC程序设计之来龙去脉
- android事件机制之来龙去脉
- 详解ORACLE ROWID之来龙去脉
- Android输入输出机制之来龙去脉
- c++动态数组(一)之new和delete
- Python编解码小结(一)—— Unicode的来龙去脉
- 【Android自助餐】Handler消息机制完全解析(一)Message中obtain()与recycle()的来龙去脉
- Linux Kernel之Deferred work(Softirq、tasklet、Work queues)来龙去脉浅析
- AppFramework通用数据库访问组件之来龙去脉
- 在Ubuntu上为Android系统编写Linux内核驱动程序
- 写一个Singleton模式的例子
- 在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序
- 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 粗解“new”之来龙去脉(一)
- 在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务
- Hibernate使用NativeSQL查询类型为char的columns
- 触发器
- 在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务
- 将一个LIST分组,转为map<string,list>类型
- 浅谈Android系统开发中LOG的使用
- 制作可独立分发的Android模拟器
- 确定组织是否真正敏捷的五种方法