选择正确的分配方法
使用以下指南为方案选择适当的内存分配 API:
| Scenario | 推荐的 API | 备注 |
|---|---|---|
| 常规用途 C++ 分配 | new / std::make_unique / std::make_shared |
C++ 代码的默认选择。 在内部使用 CRT 堆。 RAII 可确保自动清理。 |
| 常规用途 C 分配 | malloc / calloc / realloc |
C 代码的默认选择。 跨平台移植。 |
| 大于约 1 MB 或需要页面级控件的分配 | VirtualAlloc | 以页面粒度分配(通常为 4 KB)。 用于保留具有特定保护属性(PAGE_READWRITEPAGE_EXECUTE_READ等)的大型地址范围、防护页或内存。 |
| 具有自定义控件的许多小型分配(例如池分配器) | HeapAlloc / HeapCreate | 为隔离或序列化控件创建专用堆。 仅当堆是独占单线程时使用 HEAP_NO_SERIALIZE 。 |
| COM 互操作(封送内存) | CoTaskMemAlloc | 内存跨越 COM 单元边界时是必需的。 MIDL 生成的存根使用 CoTaskMemAlloc/CoTaskMemFree。 |
| 使用可移动内存的旧代码 | GlobalAlloc / LocalAlloc | 避免使用新代码。 它们增加了对 HeapAlloc 的开销,主要是为了向后兼容剪贴板操作和某些需要它们的 Win32 API。 |
Important
始终正确配对分配和解除分配函数。 分配的 HeapAlloc 内存必须释放 , HeapFree 而不是 free() 或 LocalFree。 混合分配/解除分配家庭会导致难以诊断的堆损坏。
注释
VirtualAlloc 与 HeapAlloc:VirtualAlloc 以页面粒度运行(最少 4 KB,预留与通常为 64 KB 一致 SYSTEM_INFO.dwAllocationGranularity)。 使用 VirtualAlloc 分配许多小型对象会浪费地址空间。 将 HeapAlloc(或 malloc/new)用于小型频繁分配。 如果需要保留大型地址区域、控制页面保护或实现自定义分配器,请使用 VirtualAlloc。
详细比较
下面是各种内存分配方法的简要比较:
- CoTaskMemAlloc
- GlobalAlloc
- HeapAlloc
- LocalAlloc
- Malloc
- 新增功能
- VirtualAlloc
尽管 GlobalAlloc、LocalAlloc 和 HeapAlloc 函数最终从同一堆分配内存,但每个函数提供一组略有不同的功能。 例如,如果无法分配内存,则可以指示 HeapAlloc 引发异常,这是 LocalAlloc 不可用的功能。 LocalAlloc 支持分配允许重新分配基础内存的句柄,而无需更改句柄值,这是 HeapAlloc 不可用的功能。
从 32 位Windows开始,GlobalAlloc 和 LocalAlloc 作为包装函数实现,这些包装函数使用进程的默认堆句柄调用 HeapAlloc。 因此, GlobalAlloc 和 LocalAlloc 的开销大于 HeapAlloc。
由于不同的堆分配器使用不同的机制提供独特的功能,因此必须使用正确的函数释放内存。 例如,使用 HeapAlloc 分配的内存必须使用 HeapFree 释放,而不是 LocalFree 或 GlobalFree。 必须使用相应的全局或本地函数查询、验证和释放使用 GlobalAlloc 或 LocalAlloc 分配的内存。
VirtualAlloc 函数允许指定内存分配的其他选项。 但是,其分配使用页面粒度,因此使用 VirtualAlloc 可能会导致内存使用率较高。
malloc 函数具有依赖于运行时的缺点。 新运算符具有编译器依赖性和语言依赖性缺点。
CoTaskMemAlloc 函数具有在 C、C++ 或Visual Basic中正常运行的优势。 这也是在基于 COM 的应用程序中共享内存的唯一方法,因为 MIDL 使用 CoTaskMemAlloc 和 CoTaskMemFree 封送内存。
示例
相关主题