第十八章:堆

1. 堆非常适合分配大量的小型数据.与内存映射文件和虚拟内存相比,堆是用来管理链表和树的最佳方式.

◆ 优点:让我们专心解决手头上问题,而不必理会分配粒度和页面边界这类事情.

◆ 缺点:分配和释放内存块的速度比其他方式慢.而且也无法在对物理存储器的调拨和撤销进行直接控制

   堆使用的区域时页交换文件中分配的.

此外,系统保证不管发生什么事情都以互斥访问堆(分配和释放).

用GetProcessHeap来得到进程的默认堆的句柄:

HANDLE GetProcessHeap();

2.创建额外的堆的条件:

   ● 对组件进行保护

   ● 更有效的内存管理

   ● 局部访问

   ● 避免线程同步的开销

   ● 快速释放

3.创建额外的堆的步骤:

◆ 创建:

   HANDLE HeapCreate(

   DWORD fdwoptions,//对堆进程何种操作

   SIZE_T dwInitialSize,//最开始调拨给堆的字节数(取整到CPU页面大小的整数倍)

   SIZE_T dwMaximumSize);//对所能增长的最大大小(如果为0,表示无指定上限).

函数成功时返回对的句柄.

Fdwoptions的取值:

0,

HEAP_NO_SERIALIZE(应当避免使用该标志),

HEAP_GENERATE_EXCEPTIONS(告知系统,每当在堆中分配和重新分配的内存块失败时,抛出异常)),

HEAP_CREATE_ENABLE_EXECUTE.(想要在堆中存放可执行代码).

默认情况下,对堆的访问会依次进行,是多个线程可以从同一个堆中分配和释放内存块.同时也不会存在堆数据被破坏的危险.当任何程序试图从堆中分配一块内存的时候,HeapAlloc函数必须执行以下操作:

■  遍历已分配的内存的链表和闲置内存的链表

■  找到一块足够大的闲置内存块

■  分配一块新的内存,也就是将刚刚找到的闲置内存块标记为已分配

■  将新分配的内存块添加到已分配内存中的链表中.

◆ 从堆中分配内存块

   PVOID HeapAlloc(

   HANDLE hHeap,    //堆的句柄

   DWORD  fdwFlags, //有三种标志标志

   SIZE_T dwBytes); //需要从堆中分配多少字节

标志分类:

   HEAP_ZERO_MEMORY:分配完成后将内存块清零

   HEAP_GENERATE_EXCEPTIONS:告知系统,如果堆中没有足够的内存来满足内存分配的请求,函数就会抛出异常,(如果在HeapCreate中使用此类标志将会使得系统所有调用这个函数都会抛出异常.而如果在HeapAlloc则只对本次使用)这类异常包括两类:

标识符

含义

STATUS_NO_MEMORY

分配失败的原因是因为内存不够

STATUS_ACCESS_VIOLATION

分配失败的原因是因为堆被破坏或者传入的参数不正确

使用了这个标志符时,可以在任何Heap*函数中一旦发现堆被破坏抛出异常.

HeapSetInformation(NULL,HeapEnableTerminationOnCorruption,NULL,0);

HEAP_NO_SeRIALIZE:强制系统不要把这个调用与其他线程对同一个堆的访问一次排列起来(使用默认堆时不能使用这个标志)

函数调用成功,返回内存块的地址.否则返回NULL

在分配对于内存(1M或者更多),应该避免使用堆函数.建议使用VirtualAlloc.

调整堆的大小

   PVOID HeapReAlloc(

    HANDLE hHeap,

   DWORD    fdwFlags,

   PVOID    pvMem,//内存块的当前地址

   SIZE_T dwBytes);

fdwFlags增加的标志:HEAP_REALLOC_IN_PLACEONLY:告诉函数不要移动内存块.

◆获得内存块的大小:

        SIZE_T HeapSize(

        HANDLE hHeap,

        DWORD    fdwFlags,//为0或者HEAP_NO_SERIALIZE.

        LPCVOID pvMem);

◆释放内存块

         BOOL  HeapFree(

         HANDLE hHeap,

         DWORD   fdwFlags,

         PVOID      pvMem);

这个函数可能会使堆管理器撤销一些已经调拨的物理存储器,但这并不是唯一的.

◆撤销堆

BOOL HeapDestroy(HANDLE hHeap);//只有在进程终止的时候,另外,系统不允许在进程完全终止之前销毁进程默认堆.

获取进程堆的句柄

DWORD GetProcessHeaps(

         DWORD dwNumHeaps,

         PHANDLE phHeaps);//函数返回堆的实际数目

注意:函数所返回的句柄数组中也包括进程的默认堆.

验证堆的完整性:

BOOL HeapValidate(

         HANDLE hHeap,//堆的句柄

         DWORD   fdwFlags,//0或者HEAP_NO_SERIALIZE

         LPCVOID  pvMem);//堆的内存地址

         重新组合闲置堆,使其结合在一起

         UINT HeapCompact(

                   HANDLE hHeap,

                   DWORD   fdwFlags);//0或HEAP_NO_SERIALIZE

锁定与释放堆:

BOOL HeapLock(HANDLE hHeap)


BOOL HeapUnLock(HANDLE hHeap)//一般我们不需要自己去调用HeapLock和HeapUnLock

遍历堆的内容:

BOOL HeapWalk(

         HANDLE hHeap,

         PPROCESS_HEAP_ENTRY pHeapEntry)

 

typedef struct _PROCESS_HEAP_ENTRY {
  PVOID lpData;
  DWORD cbData;
  BYTE cbOverhead;
  BYTE iRegionIndex;
  WORD wFlags;
  union {
      struct {
          HANDLE hMem;
          DWORD dwReserved[ 3 ];
      } Block;
      struct {
          DWORD dwCommittedSize;
          DWORD dwUnCommittedSize;
          LPVOID lpFirstBlock;
          LPVOID lpLastBlock;
      } Region;
  };

} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY;

开始枚举堆中的内存块时,必须把lpData设为NULL

函数返回FALSE,表明堆中已经没有更多的内存块了.

你可能感兴趣的:(第十八章:堆)