Windows系统上的VirtualAlloc, HeapAlloc和malloc,new的区别

时间:2024-03-31 22:35:43

转载自:http://blog.csdn.net/zj510/article/details/39400087


(1) VirtualAlloc

PVOID VirtualAlloc(PVOID pvAddress, SIZE_T dwSize, DWORD fdwAllocationType, DWORD fdwProtect)

VirtualAlloc是Windows提供的API,通常用来分配大块的内存。不要用该函数实现通常情况的内存分配。该函数的一个重要特性是可以预定指定地址和大小的虚拟内存空间。例如,希望在进程的地址空间中第50MB的地方分配内存,那么将参数 50*1024*`1024 = 52428800 传递给pvAddress,将需要的内存大小传递给dwSize。如果系统有足够大的闲置区域能满足请求,则系统会将该块区域预订下来并返回预订内存的基地址,否则返回NULL。

使用VirtualAlloc分配的内存需要使用VirtualFree来释放.

(2) HeapAlloc

HeapAlloc是Windows提供的API,在进程初始化的时候,系统会在进程的地址空间中创建1M大小的堆,称为默认堆(Default Heap),该大小为默认值,可以通过/HEAP连接器开关进行修改。用户也可以通过HeapCreate创建额外的堆,堆的使用可以更有效的进行内存管理,避免线程同步的开销以及快速的释放内存等。HeapAlloc用于从堆上分配一个内存块,如果分配成功则返回内存块的地址。HeapAlloc内部会根据请求的大小以及堆的大小来决定具体的实现,例如在需要大的内存空间时,会自动调用VirtualAlloc函数分配空间。该函数通常用来分配一般大小的内存空间,一些Windows API可能会要求使用该函数进行内存分配并传递给API参数。注意,在分配大的内存块时(例如1M或者更多)最好避免使用堆函数,建议使用VirtualAlloc。

使用HeapFree释放由HeapAlloc的分配的内存.

(3) malloc

C语言的内存分配函数,用于分配一般的内存空间,该函数分配的内存不会自动进行初始化。如果使用C语言编程,使用该函数。在Visual C++ 中,malloc函数会调用HeapAlloc函数。

malloc分配的内存由free函数释放。

(4) new

C++语言的实现方式,在Visual C++ 中,通过调用HeapAlloc实现内存分配,如果使用C++编程,建议使用new进行一般内存的分配。系统根据调用的方式决定是否对对象进行初始化。

注意: new 在C++中实际上是操作符而不是函数。

使用new 分配的内存由delete / delete[] 进行释放。


Windows系统上的VirtualAlloc, HeapAlloc和malloc,new的区别

内存管理有三种方式:

1. 虚拟内存,VirtualAlloc之类的函数

2. 堆,Heapxxx函数,malloc,new等

3. 内存映射文件,Memory Mapped File

很多人都会困惑,但是看下面的图片就会比较明白了。这个图片从MSDN上拷来。


堆和虚拟内存,从上面的图片就可以看出,其实所谓的堆,也就是在虚拟内存上抽象出来的。如果直接用Virtualxxx系列函数,是有一些限制的,比如每次只能分配页大小倍数的内存,内存地址也必须对齐什么的。新手很难用。正因为如此,才出现了堆。实际上堆Heap内部就是使用Virtual系列函数的。基本思想就是:先用VirtualAlloc分配一个比较大的内存,然后用户每次申请堆内存的时候,从分配出来的虚拟内存块上指定一块给用户。比如第一次分配堆内存从A地址开始的100个字节,第二次分配的时候就是A+100开始。

Memory Mapped File就不太一样了,从上面的图可以看出MMF并没有调用Virtual系列函数。它直接调用内核层了。

对于malloc和new,这2个并不是操作系统API,它们是语言提供的函数。在不同的系统上面有不同的实现方法,在Windows上面,new调用malloc,malloc调用堆函数(heapxxx),堆函数调用Virtual系列函数。在Linux上面就调用相应的linux API.


-------------------------

以前对虚拟内存一直有误解,以为虚拟内存就是所谓的(页交换文件)其实虚拟内存在操作系统上的概念是:虚拟地址空间所映射的物理内存。所谓虚拟就是(虚拟地址的意思)

另外微软操作系统中解释的虚拟内存就是所谓的(页交换文件)

虚拟地址空间(预定并调拨以后)->物理地址空间(通常是磁盘,仅仅是空间对应) 

如果需要访问一个单元的数据(比如写入)会检查其是否存在与随机内存中(RAM)如果存在则直接访问。

如果不存在随机内存,系统会直接去检查是否存在页面文件(物理地址)。如果当前随机内存中有闲置的页面,则将物理地址中的数据加载入随机内存的页面中的闲置页面。

如果当前随机内存中没有闲置的页面,则会释放一个页面(被释放的页面中的数据如果被修改过会将其写入物理地址,磁盘中)。然后将需要的数据从其物理地址读入刚被释放的页面(随机内存中)。

特例:对于内存映像文件(如exe ,dll)通常不会在页面文件中为其创建副本。而会直接将其虚拟地址隐射到磁盘文件上。

当多个进程对应同一个内存映像文件的磁盘文件时,若有进程中的线程对其地址空间做修改(比如修改一个全局静态变量),由于会影响到所对应的磁盘上的文件。此时操作系统才会在交换文件中为其创建副本,并重新映射虚拟内存中对应的物理地址(磁盘)。