There are lots of method to allocate memory in Windows environment, such as VirtualAlloc
, HeapAlloc
, malloc
, new
.
It is very important to understand the distinction between memory allocation APIs (in Windows) if you plan on using a language that requires memory management (like C or C++.) And the best way to illustrate it IMHO is with a diagram:
Note that this is a very simplified, Windows-specific view.
The way to understand this diagram is that the higher on the diagram a memory allocation method is, the higher level implementation it uses. But let's start from the bottom.
It provides all memory reservations & allocations for the operating system, as well as support for memory-mapped files, shared memory, copy-on-write operations, etc. It's not directly accessible from the user-mode code, so I'll skip it here.
These are the lowest level APIs available from the user mode. The VirtualAlloc
function basically invokes ZwAllocateVirtualMemory that in turn does a quick syscall to ring0
to relegate further processing to the kernel memory manager. It is also the fastest method to reserve/allocate block of new memory from all available in the user mode.
But it comes with two main conditions:
It only allocates memory blocks aligned on the system granularity boundary.
It only allocates memory blocks of the size that is the multiple of the system granularity.
So what is this system granularity? You can get it by calling GetSystemInfo. It is returned as the dwAllocationGranularity parameter. Its value is implementation (and possibly hardware) specific, but on many 64-bit Windows systems it is set at 0x10000
bytes, or 64K
.
So what all this means, is that if you try to allocate, say just an 8 byte memory block with VirtualAlloc
:
void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
If successful, pAddress
will be aligned on the 0x10000
byte boundary. And even though you requested only 8 bytes, the actual memory block that you will get will be the entire page
(or, something like 4K
bytes. The exact page size is returned in the dwPageSize parameter.) But, on top of that, the entire memory block spanning 0x10000
bytes (or 64K
in most cases) from pAddress
will not be available for any further allocations. So in a sense, by allocating 8 bytes you could as well be asking for 65536.
So the moral of the story here is not to substitute VirtualAlloc
for generic memory allocations in your application. It must be used for very specific cases, as is done with the heap below. (Usually for reserving/allocating large blocks of memory.)
Using VirtualAlloc
incorrectly can lead to severe memory fragmentation.
In a nutshell, the heap functions are basically a wrapper for VirtualAlloc
function. Other answers here provide a pretty good concept of it. I'll add that, in a very simplistic view, the way heap works is this:
HeapCreate
reserves a large block of virtual memory by calling VirtualAlloc
internally (or ZwAllocateVirtualMemory
to be specific). It also sets up an internal data structure that can track further smaller size allocations within the reserved block of virtual memory.
Any calls to HeapAlloc
and HeapFree
do not actually allocate/free any new memory (unless, of course the request exceeds what has been already reserved in HeapCreate
) but instead they meter out (or commit
) a previously reserved large chunk, by dissecting it into smaller memory blocks that a user requests.
HeapDestroy
in turn calls VirtualFree
that actually frees the virtual memory.
So all this makes heap functions perfect candidates for generic memory allocations in your application. It is great for arbitrary size memory allocations. But a small price to pay for the convenience of the heap functions is that they introduce a slight overhead over VirtualAlloc
when reserving larger blocks of memory.
Another good thing about heap is that you don't really need to create one. It is generally created for you when your process starts. So one can access it by calling GetProcessHeap function.
Is a language-specific wrapper for the heap functions. Unlike HeapAlloc
, HeapFree
, etc. these functions will work not only if your code is compiled for Windows, but also for other operating systems (such as Linux, etc.)
This is a recommended way to allocate/free memory if you program in C. (Unless, you're coding a specific kernel mode device driver.)
Come as a high level (well, for C++
) memory management operators. They are specific for the C++
language, and like malloc
for C
, are also the wrappers for the heap
functions. They also have a whole bunch of their own code that deals C++
-specific initialization of constructors, deallocation in destructors, raising an exception, etc.
These functions are a recommended way to allocate/free memory and objects if you program in C++
.
Lastly, one comment I want to make about what has been said in other responses about using VirtualAlloc
to share memory between processes. VirtualAlloc
by itself does not allow sharing of its reserved/allocated memory with other processes. For that one needs to use CreateFileMapping API that can create a named virtual memory block that can be shared with other processes. It can also map a file on disk into virtual memory for read/write access. But that is another topic.