91

在 Windows 环境中分配内存的方法有很多,例如VirtualAlloc, HeapAlloc, malloc, new

那么,它们之间有什么区别呢?

4

6 回答 6

90

每个 API 都有不同的用途。每一个还要求您在使用完内存后使用正确的释放/释放功能。

虚拟分配

一个低级的 Windows API,它提供了很多选项,但主要对处于相当特定情况的人有用。只能在(编辑:不是 4KB)更大的块中分配内存。在某些情况下您需要它,但是当您处于其中一种情况时您会知道。最常见的一种情况是您必须直接与另一个进程共享内存。不要将其用于通用内存分配。用于VirtualFree解除分配。

堆分配

分配您要求的任何大小的内存,而不是比VirtualAlloc. HeapAlloc知道何时需要调用VirtualAlloc并自动为您调用。类似malloc,但仅适用于 Windows,并提供了更多选项。适合分配一般的内存块。某些 Windows API 可能要求您使用它来分配您传递给它们的内存,或使用它的同伴HeapFree来释放它们返回给您的内存。

malloc

C分配内存的方式。如果您使用 C 而不是 C++ 编写,并且您希望您的代码也可以在例如 Unix 计算机上工作,或者有人特别说您需要使用它,则更喜欢这个。不初始化内存。适合分配一般的内存块,比如HeapAlloc. 一个简单的 API。用于free解除分配。Visual C++ 的malloc调用HeapAlloc.

新的

C++ 分配内存的方式。如果您使用 C++ 编写,则更喜欢这个。它也将一个或多个对象放入分配的内存中。用于delete解除分配(或delete[]用于数组)。Visual Studio 的new调用HeapAlloc,然后可能会初始化对象,具体取决于您如何调用它。

在最近的 C++ 标准(C++11 及更高版本)中,如果您必须手动使用delete,那么您做错了,应该使用类似的智能指针unique_ptr。从 C++14 开始,同样可以说new(替换为 等函数make_unique())。


还有一些其他类似的功能SysAllocString,可能会告诉您必须在特定情况下使用。

于 2009-05-16T09:57:16.967 回答
44

如果您打算使用需要内存管理的语言(如 C 或 C++),那么了解内存分配 API(在 Windows 中)之间的区别非常重要。恕我直言,最好的说明方法是使用图表:

在此处输入图像描述

请注意,这是一个非常简化的特定于 Windows 的视图。

理解这个图的方法是,内存分配方法在图上越高,它使用的实现就越高。但是,让我们从底部开始。

内核模式内存管理器

它为操作系统提供所有内存预留和分配,以及对内存映射文件共享内存写时复制操作等的支持。它不能从用户模式代码直接访问,所以我会跳过在这里。

虚拟分配/虚拟自由

这些是用户模式下可用的最低级别的API 。该函数基本上调用ZwAllocateVirtualMemory,然后执行快速系统调用以将进一步处理委托给内核内存管理器。它也是在用户模式下从所有可用内存中保留/分配新内存块的最快方法。VirtualAllocring0

但它有两个主要条件:

  • 它只分配在系统粒度边界上对齐的内存块。

  • 它只分配大小是系统粒度的倍数的内存块。

那么这个系统粒度是多少呢?您可以通过调用GetSystemInfo来获取它。它作为dwAllocationGranularity参数返回。它的值是特定于实现(也可能是硬件)的,但在许多 64 位 Windows 系统上,它设置为0x10000字节或64K.

所以这一切意味着,如果你尝试分配,说只是一个 8 字节的内存块VirtualAlloc

void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

如果成功,pAddress将在0x10000字节边界上对齐。即使您只请求了 8 个字节,您将获得的实际内存块将是整个内存块page(或类似4K字节的东西。在参数中返回确切的页面大小dwPageSize。)但是,最重要的是,整个内存块跨越0x10000字节(或64K在大多数情况下)pAddress 将不可用于任何进一步的分配。所以从某种意义上说,通过分配 8 个字节,你也可以要求 65536。

因此,这里故事的寓意并不是要替代VirtualAlloc应用程序中的通用内存分配。它必须用于非常特殊的情况,就像下面的一样。(通常用于保留/分配大块内存。)

使用VirtualAlloc不当会导致严重的内存碎片。

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

简而言之,函数基本上是VirtualAlloc函数的包装器。这里的其他答案提供了一个很好的概念。我将补充一点,在一个非常简单的视图中,的工作方式是这样的:

  • HeapCreateVirtualAlloc通过内部调用(或ZwAllocateVirtualMemory具体而言)保留一大块虚拟内存。它还建立了一个内部数据结构,可以在虚拟内存的保留块中跟踪更小尺寸的分配。

  • 对任何新内存的调用HeapAlloc并且实际上并不分配/释放任何新内存(当然,除非请求超过HeapFree已经在用户请求。HeapCreatecommit

  • HeapDestroy反过来调用VirtualFree实际上释放了虚拟内存。

因此,所有这些都使函数成为应用程序中通用内存分配的完美候选者。它非常适合任意大小的内存分配。但是为了堆函数的便利性而付出的一个小代价是,它们VirtualAlloc在保留更大的内存块时会引入轻微的开销。

的另一个好处是您实际上并不需要创建一个。它通常是在您的流程开始时为您创建的。因此可以通过调用GetProcessHeap函数来访问它。

malloc /免费

函数的特定于语言的包装器。与HeapAlloc,HeapFree等不同,这些函数不仅适用于您的代码为 Windows 编译,而且适用于其他操作系统(如 Linux 等)

如果您在 C 中编程,这是分配/释放内存的推荐方法。(除非您正在编写特定的内核模式设备驱动程序。)

新建/删除

作为高级(嗯,for C++)内存管理运算符来。它们是特定于C++语言的,就像mallocfor一样C,也是heap函数的包装器。他们也有一大堆自己的代码来处理C++构造函数的特定初始化、析构函数中的释放、引发异常等。

如果您在C++.


最后,我想就其他回复中关于使用VirtualAlloc在进程之间共享内存的内容发表评论。VirtualAlloc本身不允许与其他进程共享其保留/分配的内存。为此,需要使用CreateFileMapping可以创建可与其他进程共享的命名虚拟内存块的 API。它还可以将磁盘上的文件映射到虚拟内存中以进行读/写访问。但这是另一个话题。

于 2017-05-18T18:54:26.190 回答
28

VirtualAlloc是 OS 虚拟内存 (VM) 系统的专门分配。VM 系统中的分配必须以分配粒度进行,该分配粒度(分配粒度)取决于体系结构。VM系统中的分配是内存分配的最基本形式之一。VM 分配可以采用多种形式,内存不一定是专用的或物理支持在 RAM 中(尽管可以)。VM 分配通常是一种特殊用途的分配类型,因为分配必须

  • 非常大,
  • 需要分享,
  • 必须与特定值(性能原因)保持一致,或者
  • 调用者不需要一次使用所有这些内存......
  • ETC...

HeapAlloc本质上是malloc两者new最终调用的内容。它被设计为在通用分配的许多不同类型的场景下非常快速且可用。它是经典意义上的“堆”。堆实际上是由 a 设置的VirtualAlloc,它最初用于从操作系统保留分配空间。通过 初始化空间后VirtualAlloc,配置各种表、列表等数据结构来维护和控制 HEAP 的运行。其中一些操作采用动态调整堆大小(增长和缩小)、使堆适应特定用途(某种大小的频繁分配)等形式。

new并且malloc有些相同,malloc本质上是对HeapAlloc( heap-id-default );的确切调用 new但是,可以 [另外] 为 C++对象配置分配的内存。对于给定的对象,C++ 将为每个调用者在堆上存储 vtable。这些 vtable 是用于执行的重定向,并构成了 C++ 的 OO 特性(如继承、函数重载等)的一部分......

其他一些常见的分配方法,例如_alloca()_malloca()是基于堆栈的;FileMappings 真正分配VirtualAlloc并设置了特定的位标志,这些标志将这些映射指定为 type FILE

大多数时候,您应该以与该内存的使用一致的方式分配内存;)。 new在 C++ 中,malloc对于 C,VirtualAlloc对于海量或 IPC 案例。

*** 注意,由 完成的大内存分配HeapAlloc实际上是VirtualAlloc在一定大小(几百 k 或 16 MB 或我忘记的东西,但相当大 :) )之后运送的。

*** 编辑我简要地评论了 IPC 和VirtualAlloc,还有一些非常简洁的相关VirtualAlloc内容,对此问题的回复者都没有讨论过。

VirtualAllocEx是一个进程可以用来在不同进程的地址空间中分配内存的方法。最典型的是,这用于通过CreateRemoteThread组合使用以在另一个进程的上下文中远程执行(类似于,线程只是在另一个进程中运行)。CreateThread

于 2009-05-16T09:53:33.293 回答
7

概述:

  • VirtualAlloc、HeapAlloc 等是直接从操作系统分配各种类型内存的 Windows API。VirtualAlloc 管理 Windows 虚拟内存系统中的页面,而 HeapAlloc 从特定的 OS 堆分配。坦率地说,您不太可能需要使用它们中的任何一个。

  • malloc 是一个标准 C(和 C++)库函数,可为您的进程分配内存。malloc 的实现通常会使用其中一个 OS API 在您的应用程序启动时创建一个内存池,然后在您发出 malloc 请求时从中分配

  • new 是一个标准 C++ 运算符,它分配内存,然后在该内存上适当地调用构造函数。它可以根据 malloc 或 OS API 来实现,在这种情况下,它通常也会在应用程序启动时创建一个内存池。

于 2009-05-16T09:44:13.893 回答
4

VirtualAlloc===> sbrk()UNIX下

HeapAlloc====> malloc()UNIX下

于 2010-01-18T00:12:34.060 回答
2

VirtualAlloc=> 直接分配到虚拟内存,你保留/提交块。这对于大型分配非常有用,例如大型数组。

HeapAlloc/ new=> 在默认堆(或您可能创建的任何其他堆)上分配内存。这为每个对象分配,非常适合较小的对象。默认堆是可序列化的,因此它具有保证线程分配(这可能会在高性能场景中导致一些问题,这就是您可以创建自己的堆的原因)。

malloc=> 使用 C 运行时堆,类似于HeapAlloc但在兼容性场景中很常见。

简而言之,堆只是由堆管理器管理的一块虚拟内存(而不是原始虚拟内存)

内存世界的最后一个模型是内存映射文件,这种场景非常适合大块数据(如大文件)。这在您打开 EXE 时在内部使用(它不会将 EXE 加载到内存中,只是创建一个内存映射文件)。

于 2009-06-17T14:14:15.437 回答