实验环境:win764位旗舰版、VS2010旗舰版
每个进程都有一个默认堆,在进程初始化的时候会创建这个默认堆,可以通过GetProcessHeap()获取默认堆的句柄。使用CRT时,也会有一个CRT堆,VS项目属性 ~ C/C++ ~ 代码生成 ~ 运行库,如果选择多线程DLL,则CRT堆初始化在DLL中,如果选择多线程,则会在进入_tmain函数之前。
运行库连接方式设置:
CRT堆初始化:
位于VS目录Microsoft Visual Studio 10.0VCcrtsrcheapinit.c文件中有一个_heap_init函数,可以设置断点查看CRT堆初始化过程。
int __cdecl _heap_init (void) { ULONG HeapType = 2; // Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate(0, BYTES_PER_PAGE, 0)) == NULL ) return 0; #ifdef _WIN64 // Enable the Low Fragmentation Heap by default on Windows XP and // Windows Server 2003. It's the 8 byte overhead heap, and has // generally better performance charateristics than standard heap, // particularly for apps that perform lots of small allocations. if (LOBYTE(GetVersion()) < 6) { HeapSetInformation(_crtheap, HeapCompatibilityInformation, &HeapType, sizeof(HeapType)); } #endif /* _WIN64 */ return 1; }
注意:这里调用HeapCreate函数的第一个参数使用0,而没有使用HEAP_NO_SERIALIZE标志,则表示对堆的访问时独占的,即默认情况下使用new和delete是线程安全的。
c++使用new是在CRT堆上分配内存:
位于VS目录Microsoft Visual Studio 10.0VCcrtsrcmalloc.c文件中有一个_heap_alloc函数
__forceinline void * __cdecl _heap_alloc (size_t size) { if (_crtheap == 0) { _FF_MSGBANNER(); /* write run-time error banner */ _NMSG_WRITE(_RT_CRT_NOTINIT); /* write message */ __crtExitProcess(255); /* normally _exit(255) */ } return HeapAlloc(_crtheap, 0, size ? size : 1); }
经常听到高手说,最好不要跨模块new和delete,否则将会出现严重的错误(堆被破坏)。例如在DLL中使用new分配一块内存,让后在EXE中使用delete释放这块内存,有可能堆会被破坏,原因是,如果EXE和DLL都是用静态库的方式链接运行时库,此时EXE和DLL各自将有一个CRT堆,在一个堆上分配内存,让后再另一个堆上释放内存是肯定会发生错误的。如果EXE和DLL都是用动态库的方式链接运行时库,他们使用的是同一个CRT堆,则不会发错误。