为什么要创建额外的堆

 

除了进程的默认堆,我们还可以在进程的地址空间中创建额外的堆。原因是:

1.对组件进行保护

2.更有效的内存管理

3.局部访问

4.避免线程同步的开销

5.快速释放

下面分析第一个原因:假设应用程序需要处理2个组件,一个由NODE结构组成的链表和一个由BRANCH结构组成的二叉树。我们有2个源文件LinkList.cpp和BinTree.cpp,分别包含处理链表和二叉树的函数。

如果NODE和BRANCH结构都保存在同一个堆中,那么这么混合的堆看起来会像上图描绘的那样。现在假如链表的代码有一个BUG,覆盖了NODE1后的8个字节,从而破坏了BRANCH3中的数据,BinTree.cpp中的代码后来在遍历二叉树时,可能由于这个原因而失败。当然这会造成一种假象,认我们不由地想链表的代码有BUG,可以看出,不同类型的对象混杂在同一堆中,对BUG的定位和跟踪带来困难。

通过创建2个独立的堆,一个保存NODE结构,一个保存BRANCH结构,我们可以使问题局部化。链表中的一个小BUG不会破坏二叉树的完整性,反之亦然。虽然还存在另一种可能性,即有BUG的代码写入到另一个堆的内存,但这种概率要小很多。

下面讨论一下更有效的内存管理。如果始终都从堆中分配同样大小的对象,我们就可以对它们进行更加有效的管理。如每个NODE结构需要24字节,每个BRANCH结构需要32字节,所有对象都从一个堆中分配时,如果NODE对象和BRANCH对象占满后,释放2个不连续的NODE,堆中出现的内存碎片将不能很好地利用:这时我们如果想分配一个BRANCH结构,没地了。。。有48字节也没用,不连续,放不下。。。如果每个堆只包含同样大小的对象,释放一个就能容纳一个新的。。。(规范化的问题

内存访问局部化:当系统必须把一个内存页面换出到页交换文件,或把页交换文件中的一个页面换入到内存中时,会对性能产生非常大的影响。。。(深有体会。。。硬盘疯狂咯咯作响。。。)如果把内存访问局限在一个较小的地址区间内,将降低系统需要在内存和磁盘间进行交换的可能性。

因此,设计应用程序时,一种很好的做法是把需要同时访问的对象分配在相邻的内存地址。回到刚才的例子,遍历链表和遍历二叉树之间不存在任何关联。通过在同一个堆中的相邻的内存地址分配NODE对象,可以把NODE对象放到邻近的页面中。事实上,很有可能多个NODE对象都在同一个物理内存页面中。这样在遍历链表时,CPU就不需要在访问每个NODE时去访问内存中的不同页面了。

如果在同一个堆中分配NODE和BRANCH对象,NODE对象不一定相邻,最差时,每个内存页只有一个NODE对象,这种情况下遍历链表中每个NODE都会引起页面错,导致整个遍历过程变得特慢。

线程同步开销的避免:默认情况下,对堆的访问是依次进行的。这样即使在同一时刻有多个线程要访问堆,也不会出现数据被破坏的现象。但是,堆函数必须执行额外的代码来保证堆的线程安全性(Thread-Safe)。如果从堆中进行大量的分配操作,那么执行这些额外的代码就不得了。。。如果创建一个新堆,就可以告诉系统只有一个线程访问堆,这样堆函数就不必执行额外的代码了,但这时我们要管线程安全性的事了。

把一些数据结构存入一个单独的堆中,可以在不用时直接释放,而不用显式地释放堆中的每个内存块。如当Windows资源管理器在遍历硬盘上的目录层次时,它必须在内存中建立一棵树。如果我们让Windows资源管理器刷新显示,它会直接销毁包含整棵树的那个堆,然后再来一遍。对许多应用程序来说,这不但方便而且高效。

如何创建额外的堆:调用HeapCreate在自己的进程中创建额外的堆

HANDLE HeapCreate(

DWORD fdwOptions,

SIZE_T dwInitialSize,

SIZE_T dwMaximumSize);

fdwOptions用来表示对堆的操作如何进行。可以指定0或HEAP_NO_SERIALIZE或HEAP_GENERATE_EXCEPTIONS,HEAP_CREATE_ENABLE_EXECUTE或这些标志的组合。

 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值