VirtualAlloc和HeapAlloc之间有什么区别?

时间:2009-05-16 09:38:41

标签: winapi memory heap new-operator

在Windows环境中有很多方法可以分配内存,例如VirtualAllocHeapAllocmallocnew

因此,它们之间的区别是什么?

6 个答案:

答案 0 :(得分:79)

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

虚拟

提供大量选项的低级Windows API,但主要适用于相当特定情况下的用户。只能在(编辑:不是4KB)更大的块中分配内存。有些情况下你需要它,但你会知道你何时处于这些情况之一。最常见的一种情况是,您必须直接与另一个进程共享内存。不要将它用于通用内存分配。使用VirtualFree取消分配。

HeapAlloc

分配你要求的任何大小的内存,而不是大块VirtualAllocHeapAlloc知道何时需要致电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,那么你做错了,应该使用智能指针,如{{ 1}}而不是。从C ++ 14开始,unique_ptr也可以这样说(用new之类的函数代替)。


还有一些其他类似的功能,如make_unique(),您可能会被告知必须在特定情况下使用。

答案 1 :(得分:28)

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

  • 非常大,
  • 需要分享,
  • 必须与特定值(性能原因)或
  • 对齐
  • 来电者不需要一次使用所有这些内存......
  • 等...

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

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

其他一些常见的分配方法,如_alloca()_malloca(),基于 stack ; FileMappings实际上是使用VirtualAlloc分配的,并使用特定的位标志进行设置,这些位标志将这些映射指定为FILE类型。

大多数情况下,您应该以与使用该内存一致的方式分配内存;)。 C ++为new,C为malloc,大量或IPC为VirtualAlloc

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

***编辑 我简要地谈到了IPC和VirtualAlloc,对于一个相关的VirtualAlloc,还有一些非常简洁的问题,这个问题没有一个响应者讨论过。

VirtualAlloc Ex 是一个进程可用于在不同进程的地址空间中分配内存的内容。最典型的情况是,使用组合通过 CreateRemoteThread 在另一个进程的上下文中进行远程执行(类似于CreateThread,该线程只是在其他过程)。

答案 2 :(得分:21)

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

enter image description here

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

理解此图的方法是,图中的内存分配方法越高,它使用的更高级实现。但是,让我们从底部开始。

内核模式内存管理器

它提供所有内存预留&操作系统的分配,以及对内存映射文件共享内存 copy-on-write 操作等的支持。 #39;不能直接从用户模式代码访问,因此我将在此处跳过它。

VirtualAlloc / VirtualFree

这些是user mode提供的最低级 API。 VirtualAlloc函数基本上调用ZwAllocateVirtualMemory,然后快速 syscall ring0,以进一步处理内核内存管理器。它也是在用户模式下保留/分配新内存块的最快方法。

但它有两个主要条件:

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

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

那么系统粒度是什么?你可以致电GetSystemInfo来获得它。它作为dwAllocationGranularity参数返回。它的值是实现(可能是硬件)特定的,但在许多64位Windows系统上,它设置为0x10000字节或64K

所有这一切意味着,如果你试图分配,只说一个带有VirtualAlloc的8字节内存块:

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

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

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

错误地使用VirtualAlloc会导致严重的内存碎片。

dwPageSize / HeapCreate / HeapAlloc / HeapFree

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

  • HeapCreate通过在内部调用VirtualAlloc(或ZwAllocateVirtualMemory具体)来保留大量虚拟内存。它还设置了一个内部数据结构,可以在保留的虚拟内存块中跟踪更小的大小分配。

  • HeapAllocHeapFree的任何调用都不会实际分配/释放任何新内存(当然,除非请求超出HeapCreate中已保留的内容),相反,他们计算出(或commit)先前保留的大块,通过将其解析为用户请求的较小内存块。

  • HeapDestroy依次调用实际释放虚拟内存的VirtualFree

所以这一切使 heap 函数成为应用程序中通用内存分配的理想选择。它适用于任意大小的内存分配。但是,为了方便功能而付出的一小代价是,当保留更大的内存块时,它们会在VirtualAlloc上引入一点点开销。

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

GetProcessHeap / free

函数的特定于语言的包装器。与HeapAllocHeapFree等不同,这些函数不仅适用于您的代码是针对Windows编译的,还适用于其他操作系统(例如Linux等)

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

malloc /删除

高级别(对于C++)内存管理运营商来说。它们特定于C++语言,与malloc C类似,也是heap函数的包装器。他们还有一大堆自己的代码来处理C++ - 构造函数的特定初始化,析构函数中的释放等等。

如果您在C++进行编程,这些函数是分配/释放内存和对象的推荐方法。

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

答案 3 :(得分:7)

概述:

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

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

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

答案 4 :(得分:4)

VirtualAlloc ===> UNIX下的sbrk()

HeapAlloc ====> UNIX下的malloc()

答案 5 :(得分:2)

VirtualAlloc =>直接分配到虚拟内存中,您可以在块中保留/提交。这对于大型分配很有用,例如大型数组。

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

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

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

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