第十八章:堆

  1:基础

1:堆就是一块预订的地址空间区域,系统会根据请求调拨物理存储器,堆很适合管理小型对象,使用堆不必理会分配粒度和页面大小

2:堆是进程相关的,一个进程可以有多个堆,默认堆的大小为1M,默认堆是线程安全的,所以如果一个进程只有一个线程,为了略微提高性能,应该创建自己的堆

3:默认堆是肯定会被创建的,API会使用默认堆

4:HANDLE GetProcessHeap();返回进程默认堆句柄

5:DLL没有堆

2:创建自定义堆的好处

假设有一个链表和一个二叉树

(1)安全:将链表和二叉树放在不同的堆中,这样能避免他们相互影响,防止链表错误影响到二叉树

(2)高效:如果每个堆都只包含相同大小的对象,效率更高并且不会产生内存碎片

(3)同样是高效:我们经常访问相邻的数据,比我我们会循环遍历链表,如果他们放在不同的堆中,我们就能在同一个页面访问更多的数据,并且,当CPU将页面唤出到页交换文件时,也会产生同样的特性,这样我们也相对的减少了内存和页交换文件交互的此处(这段话可能说的不明白,看不懂就看书吧)

(4)如果创建一个新堆,并告诉操作系统只有一个线程访问此堆,这样就节省了线程同步的开销

(5)如果我们在一个自己的堆中存特定的数据,当我们不需要这些数据时,可以直接释放整个堆而不必显式释放每个内存块

3:创建堆

HANDLE HeapCreate(
	DWORD flOptions,
	SIZE_T dwInitialSize,	//初始调拨给堆的字节数,操作系统可能会向上取整到页面大小整数倍
	SIZE_T dwMaximumSize	//堆最大大小,如果不为0,超过最大大小会抛出异常,如果为0堆可以无限增长
	);

//flOptions取值可为
0							//默认属性
HEAP_NO_SERIALIZE			//创建线程不安全堆
HEAP_CREATE_ENABLE_EXECUTE	//堆中可以执行代码,如果不设置此标志而执行其中的代码会抛出
							//EXCEPTION_ACCESS_VIOLATION异常
HEAP_GENERATE_EXCEPTIONS	//在堆中分配和重新分配内存块失败时抛出异常

如果系统发现堆被破坏,会引发一个断言,此外,我们可以调用一个函数,让系统发现堆被破坏产生一个异常

4:从堆中分配内存块

LPVOID HeapAlloc(
  HANDLE hHeap,
  DWORD dwFlags,
  SIZE_T dwBytes
);

//dwFlags取值可为:
HEAP_ZERO_MEMORY			//清零
HEAP_GENERATE_EXCEPTIONS	//如果本次分配不成功则抛出一个异常,如果HeapCreate()和这里都未指定此标志
							//分配不成功则返回NULL
HEAP_NO_SERIALIZE			//本次分配不需要保证线程安全

//抛出的异常可能如下
STATUS_NO_MEMORY			//堆内存不够
STATUS_ACCESS_VIOLATION		//堆被破坏或者参数出错

1:如果分配大于1M的内存,应该使用VirtualAlloc()而不使用堆

2:低碎片堆

如果堆要分配大量大小不同的对象,建议使用低碎片堆,低碎片堆不能和HEAP_NO_SERIALIZE标志同时使用

堆管理器自己也会进行优化检查,会决定是否将堆切换为低碎片堆

ULONG value=2;
if(HeapSetInformation(hHeap,HeapCompatibilityInformation,&value,sizeof(value)))
{
	//切换到低碎片堆成功
	//hHeap也可以为GetProcessHeap(),也就是将进程默认堆切换为低碎片堆
}

如果代码运行在调试器下,一些调试选项可能会阻止切换到低碎片堆,可以把环境变量_NO_DEBUG_HEAP设为1,就可以关闭这些与堆相关的调试选项

5:调整内存块的大小

注意这里是调整内存块大小不是调整堆大小

LPVOID HeapReAlloc(		//调整大小失败返回NULL
	HANDLE hHeap,
	DWORD dwFlags,		
	LPVOID lpMem,		//想要调整的内存块的当前地址
	SIZE_T dwBytes		//调整的新大小
	);

//dwFlags可以为如下值:
HEAP_GENERATE_EXCEPTIONS
HEAP_NO_SERIALIZE
HEAP_ZERO_MEMORY			//只有在增大堆时才有效,增大的部分清零
HEAP_REALLOC_IN_PLACE_ONLY	//只有在增大堆时才有效,告诉系统不要移动以前的内存块
//HeapReAlloc()可能在堆内部移动内存块,但这有可能破坏其中比如链表的数据,因为链表指向的
//指针堆移动后无效了,所以应该指定此标志,但指定了此标志,也就是说,如果堆在不移动的前提下
//增大可能会失败

6:获得内存块大小

SIZE_T HeapSize(
	HANDLE hHeap,
	DWORD dwFlags,	//0或者HEAP_NO_SERIALIZE
	LPCVOID lpMem
	);

7:释放内存块

BOOL HeapFree(
	HANDLE hHeap,
	DWORD dwFlags,	//0或者HEAP_NO_SERIALIZE
	LPVOID lpMem
	);
//调用此函数可能会使堆管理器撤销调拨的物理存储器,但这并不是一定的

8:销毁堆

BOOL HeapDestroy(HANDLE hd);

进程终止后会销毁所有堆,线程终止时,系统不会帮我们销毁堆

不能用此函数去销毁默认堆,会直接返回FALSE

9:在C++中使用堆,参见书上

10:一些其他的堆函数

GetProcessHeaps()获得进程所有堆的句柄,HeapValidate()验证堆的完整性,HeapCompact()将堆中所有闲置内存块拼贴起来

HeapLock()和HeapUnlock()锁定堆,是其线程安全,我们如果没有指定HEAP_NO_SERIALIZE属性,调用许多堆函数,其内部同样会调用HeapLock()和HeapUnlock()函数

HeapWalk()用于调试,允许我们遍历堆的内容

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值