关于堆分配的几个函数:
(1)GetProcessHeap
用以获取和调用进程的堆句柄.
typedef struct
{
char data[128];
}Data,*LPData;
LPData lpData;
lpData = (LPData)
HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(Data));
即在
堆内存上申请了一个不可移动的空间,大小为sizeof(Data).
(2).HeapAlloc
HeapAlloc是一个Windows API函数。它用来在指定的堆上分配内存,并且分配后的内存不可移动。
返回值:
(2).HeapAlloc
HeapAlloc是一个Windows API函数。它用来在指定的堆上分配内存,并且分配后的内存不可移动。
LPVOID HeapAlloc(
HANDLE hHeap,
DWORD dwFlags,
SIZE_T dwBytes,
);
hHeap
要分配堆的句柄,可以通过HeapCreate()函数或GetProcessHeap()函数获得。
dwFlags
堆分配时的可选参数,其值可以为以下的一种或多种:
|
值
|
意义
|
|---|---|
|
HEAP_GENERATE_EXCEPTIONS
|
如果分配错误将会抛出异常,而不是返回NULL。异常值可能是STATUS_NO_MEMORY, 表示获得的内存容量不足,或是STATUS_ACCESS_VIOLATION,表示存取不合法。
|
|
HEAP_NO_SERIALIZE
|
不使用连续存取。
|
|
HEAP_ZERO_MEMORY
|
将分配的内存全部清零。
|
dwBytes
要分配堆的字节数。
返回值:
如果成功分配内存,返回值为一个指向所分配内存块的首地址的(void*)指针。
如果分配内存失败,并且没有指定HEAP_GENERATE_EXCEPTIONS,则返回NULL。
|
异常代码
|
描述
|
|---|---|
|
STATUS_NO_MEMORY
|
由于缺少可用内存或者是堆损坏导致分配失败。
|
|
STATUS_ACCESS_VIOLATION
|
由于堆损坏或者是不正确的函数参数导致分配失败。.
|
plfTable = (PIP_INTERFACE_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PIP_INTERFACE_INFO));
// ... (your operations)
HeapFree(GetProcessHeap(), 0, plfTable); // 使用完毕后释放堆
注:PIP_INTERFACE_INFO结构包含了与IPv4网络接口适配器,在本地系统上启用的列表
(3)HeapCreate
具体操作
(3)HeapCreate
你可以在进程中创建辅助堆栈,方法是让线程调用HeapCreate( );函数:
HANDLE WINAPI HeapCreate(
__in DWORD flOptions,
__in SIZE_T dwInitialSize,
__in SIZE_T dwMaximumSize );
第一个参数f d w O p t i o n s用于修改如何在堆栈上执行各种操作。你可以设定0、HEAP_NO_SERIALIZE、HEAP_GENERATE_EXCEPTIONS、HEAP_CREATE_ENABLE_EXECUTE或者是这些标志的组合。
HEAP_NO_SERIALIZE:对堆的访问是非独占的,如果一个线程没有完成对堆的操作,其它线程也可以进程堆操作,这个开关是非常危险的,应尽量避免使用。
HEAP_GENERATE_EXCEPTIONS:当推分配内存失败时,会抛出异常。如果不设置,则返回NULL。
按照默认设置,堆栈将顺序访问它自己,这样,多个线程就能够分配和释放堆栈中的内存
块而不至于破坏堆栈。
当试图从堆栈分配一个内存块时, H e a p A l l o c函数(下面将要介绍)必
须执行下列操作:
1) 遍历分配的和释放的内存块的链接表。
2) 寻找一个空闲内存块的地址。
3) 通过将空闲内存块标记为“已分配”分配新内存块。
4) 将新内存块添加给内存块链接表。
下面这个例子说明为什么应该避免使用H E A P N O S E R I A L I Z E标志。假定有两个线程试
图同时从同一个堆栈中分配内存块。线程1执行上面的第一步和第二步,获得了空闲内存块的
地址。但是,在该线程可以执行第三步之前,它的运行被线程2抢占,线程2得到一个机会来
执行上面的第一步和第二步。由于线程1尚未执行第三步,因此线程2发现了同一个空闲内存块
的地址。
由于这两个线程都发现了堆栈中它们认为是空闲的内存块,因此线程1更新了链接表,给
新内存块做上了“已分配”的标记。然后线程2也更新了链接表,给同一个内存块做上了“已
分配”标记。到现在为止,两个线程都没有发现问题,但是两个线程得到的是完全相同的内存
块的地址。
这种类型的错误是很难跟踪的,因为它不会立即表现出来。相反,这个错误会在后台等待
着,直到很不适合的时候才显示出来。可能出现的问题是:
· 内存块的链接表已经被破坏。在试图分配或释放内存块之前,这个问题不会被发现。
· 两个线程共享同一个内存块。线程1和线程2会将信息写入同一个内存块。当线程1查看该
内存块的内容时,它将无法识别线程2提供的数据。
· 一个线程可能继续使用该内存块并且将它释放,导致另一个线程改写未分配的内存。这
将破坏该堆栈。
解决这个问题的办法是让单个线程独占对堆栈和它的链接表的访问权,直到该线程执行了
对堆栈的全部必要的操作。如果不使用H E A P _ N O _ S E R I A L I Z E标志,就能够达到这个目的。
只有当你的进程具备下面的一个或多个条件时,才能安全地使用H E A P N O S E R I A L I Z E标志:
· 你的进程只使用一个线程。
· 你的进程使用多个线程,但是只有单个线程访问该堆栈。
· 你的进程使用多个线程,但是它设法使用其他形式的互斥机制,如关键代码段、互斥对
象和信标(第8、9章中介绍),以便设法自己访问堆栈。
如果对是否可以使用H E A P N O S E R I A L I Z E标志没有把握,那么请不要使用它。如果不
使用该标志,每当调用堆栈函数时,线程的运行速度会受到一定的影响,但是不会破坏你的堆
栈及其数据。
454计计第三部分内存管理
下载
另一个标志H E A P G E N E R AT E E X C E P T I O N S,会在分配或重新分配堆栈中的内存块的尝
试失败时,导致系统引发一个异常条件。所谓异常条件,只不过是系统使用的另一种方法,以
便将已经出现错误的情况通知你的应用程序。有时在设计应用程序时让它查看异常条件比查看
返回值要更加容易些。异常条件将在第2 3、2 4和2 5章中介绍。
H e a p C r e a t e的第二个参数d w I n i t i a l S i z e用于指明最初提交给堆栈的字节数。如果必要的话,
H e a p C r e a t e函数会将这个值圆整为C P U页面大小的倍数。最后一个参数d w M a x i m u m S i z e用于指
明堆栈能够扩展到的最大值(即系统能够为堆栈保留的地址空间的最大数量)。如果
d w M a x i m u m S i z e大于0,那么你创建的堆栈将具有最大值。如果尝试分配的内存块会导致堆栈
超过其最大值,那么这种尝试就会失败。
如果d w M a x i m u m S i z e的值是0,那么可以创建一个能够扩展的堆栈,它没有内在的限制。
从堆栈中分配内存块只需要使堆栈不断扩展,直到物理存储器用完为止。如果堆栈创建成功,
H e a p C r e a t e函数返回一个句柄以标识新堆栈。该句柄可以被其他堆栈函数使用。
若要从堆栈中分配内存块,只需要调用H e a p A l l o c函数
理解Windows系统中的堆分配函数

本文主要探讨了Windows环境下堆内存管理的三个关键函数:HeadAlloc、GetProcessHeap和HeapCreate。GetProcessHeap函数用于获取当前进程的默认堆句柄,以便后续进行内存分配操作。HeapCreate则允许程序创建并初始化自己的堆,提供更灵活的内存管理方案。HeadAlloc是进行堆内存分配的实用函数,它从指定的堆中为应用程序分配内存。
369

被折叠的 条评论
为什么被折叠?



