比较内存分配方法

选择正确的分配方法

使用以下指南为方案选择适当的内存分配 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。

详细比较

下面是各种内存分配方法的简要比较:

尽管 GlobalAllocLocalAlloc 和 HeapAlloc 函数最终从同一堆分配内存,但每个函数提供一组略有不同的功能。 例如,如果无法分配内存,则可以指示 HeapAlloc 引发异常,这是 LocalAlloc 不可用的功能。 LocalAlloc 支持分配允许重新分配基础内存的句柄,而无需更改句柄值,这是 HeapAlloc 不可用的功能。

从 32 位Windows开始,GlobalAllocLocalAlloc 作为包装函数实现,这些包装函数使用进程的默认堆句柄调用 HeapAlloc。 因此, GlobalAllocLocalAlloc 的开销大于 HeapAlloc

由于不同的堆分配器使用不同的机制提供独特的功能,因此必须使用正确的函数释放内存。 例如,使用 HeapAlloc 分配的内存必须使用 HeapFree 释放,而不是 LocalFree 或 GlobalFree 必须使用相应的全局或本地函数查询、验证和释放使用 GlobalAllocLocalAlloc 分配的内存。

VirtualAlloc 函数允许指定内存分配的其他选项。 但是,其分配使用页面粒度,因此使用 VirtualAlloc 可能会导致内存使用率较高。

malloc 函数具有依赖于运行时的缺点。 运算符具有编译器依赖性和语言依赖性缺点。

CoTaskMemAlloc 函数具有在 C、C++ 或Visual Basic中正常运行的优势。 这也是在基于 COM 的应用程序中共享内存的唯一方法,因为 MIDL 使用 CoTaskMemAllocCoTaskMemFree 封送内存。

示例

全局函数和本地函数

堆函数

虚拟内存函数