首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VirtualAlloc和HeapAlloc有什么不同?

VirtualAlloc和HeapAlloc有什么不同?
EN

Stack Overflow用户
提问于 2009-05-16 09:38:41
回答 5查看 53.2K关注 0票数 91

Windows环境下的内存分配方法有很多,如VirtualAllocHeapAllocmallocnew等。

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

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2009-05-16 09:57:17

每个API都有不同的用途。当你使用完内存时,每一个都要求你使用正确的释放/释放函数。

VirtualAlloc

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

HeapAlloc

分配您请求的任何大小的内存,而不是以比VirtualAlloc更大的块。HeapAlloc知道何时需要调用VirtualAlloc,并自动为您执行此操作。与malloc类似,但仅适用于Windows,并提供了更多选项。适用于分配一般的内存块。一些Windows可能要求您使用它来分配传递给它们的内存,或者使用它的配套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,你可能会被告知在特定情况下必须使用。

票数 90
EN

Stack Overflow用户

发布于 2017-05-19 02:54:26

如果您计划使用需要内存管理的语言(如C或C++),那么理解内存分配API之间的区别(在Windows中)是非常重要的。而最好的解释方式是用图表来说明:

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

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

内核模式内存管理器

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

VirtualAlloc / VirtualFree

这些是user mode提供的最低级别的API。VirtualAlloc函数主要调用ZwAllocateVirtualMemory,后者会快速到ring0,以便将进一步的处理交给内核内存管理器。这也是在用户模式下从所有可用内存中保留/分配新内存块的最快方法。

但它有两个主要条件:

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

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

所有这些都意味着,如果您尝试使用VirtualAlloc分配一个8字节的内存块

代码语言:javascript
复制
void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

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

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

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

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

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

  • HeapCreate通过在内部调用VirtualAlloc (具体地说是ZwAllocateVirtualMemory )保留了一大块虚拟内存。它还设置了一个内部数据结构,可以跟踪保留的虚拟内存块中的更小大小的分配。
  • HeapAllocHeapFree的任何调用实际上并不分配/释放任何新的内存(当然,除非请求超过了HeapCreate中已经保留的内存),而是通过将其分解成更小的内存块,将其计量(或commit),用户a反过来调用VirtualFree来实际释放虚拟内存。

因此,所有这些都使得堆函数成为应用程序中通用内存分配的理想候选者。它非常适合任意大小的内存分配。但是,为方便使用堆函数而付出的一个小代价是,在保留较大的内存块时,堆函数会引入比VirtualAlloc略高的开销。

堆的另一个好处是你并不真正需要创建它。它通常是在进程启动时为您创建的。所以我们可以通过调用GetProcessHeap函数来访问它。

malloc /免费

是堆函数的特定于语言的包装器。与HeapAllocHeapFree等不同的是,这些函数不仅适用于Windows,而且适用于其他操作系统(如Linux等)。

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

new / delete

作为一个高级的(好吧,对于C++)内存管理操作员。它们是特定于C++语言的,就像Cmalloc一样,它们也是heap函数的包装器。他们也有一大堆自己的代码来处理构造函数的C++-specific初始化,解构函数中的释放,引发异常等。

如果您在C++中编程,建议使用这些函数来分配/释放内存和对象。

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

票数 38
EN

Stack Overflow用户

发布于 2009-05-16 09:44:14

在大纲中:

  • VirtualAlloc、HeapAlloc等都是直接从操作系统分配各种类型内存的Windows。VirtualAlloc管理Windows虚拟内存系统中的页面,而HeapAlloc从特定的操作系统堆中分配页面。
  • malloc是一个标准的C(和C++)库函数,它为你的进程分配内存。
  • 的实现通常会在应用程序启动时使用其中一个操作系统C++来创建一个内存池,然后在您发出malloc请求时从该内存池中进行分配。
  • new是一个标准的API操作符,它分配内存,然后在该内存上适当地调用构造函数。它可以通过malloc或OS API来实现,在这种情况下,它通常也会在应用程序启动时创建内存池。
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/872072

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档