十八 堆

来源:互联网 发布:微软公司的软件 编辑:程序博客网 时间:2024/04/29 08:28

堆非常适合大量的小型数据,优点在于我们可以专心的做我们的事情,而不用理会底层分配粒度和内存边界问题。 缺点在于分配和释放内存的速度较慢,且无法对物理存储器的调拨和撤销调拨直接控制。

堆在系统中是一块预定的地址空间区域,随着我们不断的从对中分配内存,堆管理器会给堆分配越来越多的物理存储器(始终是从页交换文件中分配的)。


18.1 进程的默认堆

进程初始化时,系统会自动在进程地址空间中创建一个堆——默认堆,默认大小为1MB。因为许多Windows函数都会用到进程堆,而许多线程可能同时调用同一Windows函数,因此对进程默认堆的访问必须是一次进行的(性能影响)。 一个进程同时可以有多个堆,在进程生命周期间可以创建或者销毁这些堆,但是默认堆是由系统自动创建和自动销毁的。


为什么要创建额外的堆:

(1)对组件的保护:比如程序要处理两个数据结构:一个链表NODE,一个二叉树BRANCH,如果将两种数据结构放入同一个堆中(其节点对象可能交叉而不能连续存放),如果在操作比如链表时,可能会因为错误操作而破坏二叉树的数据对象。如果把不同的数据组件分别存在不同的堆中就可以避免对数据组件的破坏。

(2)更有效的内存管理: 将多钟不同大小的数据对象放入同一个堆中,当其中的某一种数据对象释放一些空间后,不能恰好由另一大小的数据对象从新分配利用内存空间,从而导致内存碎片。

(3)使内存访问局部化:当系统把一个内存片交换到页交换文件或把页交换文件的一个页面交换到内存中时,会对性能产生很大影响。(进程堆始终是从页交换文件中分配的)。如果把内存访问地址局限在一个较小的区间内,可以减小内存与磁盘页交换文件之间的切换机会,在设计程序时应该把需要同时访问的数据对象存放在相邻的内存地址空间上,如上文中的链表和二叉树结构,应该分别存放在一个连续的地址空间中而不是交叉存放,这样比如在遍历链表的时候就可以减少内存页面的切换次数,提高性能。

(4)避免线程同步的开销:在同一进程中,多个线程访问堆必须是一次进行的,以免破坏堆中数据,但这会影响性能。 如果有多个堆,则多个线程就可以同时分别对多个堆进行访问,而不用依次进行访问。

(5)快速释放: 把一个数据结构的数据对象(如链表的所有对象节点)都存放在同一堆中,这样就可以直接释放整个堆来释放链表,而不用分别释放每个节点对象。


基本的函数 创建额外堆:HeapCreate 销毁堆:HeapDestory  从堆中分配内存块:HeapAlloc  释放内存块:HeapFree。


在C++中使用进程堆:

在C++中使用new关键字来创建类对象: 调用new操作函数(类重载的new操作符成员函数,如果没有重载则调用标准c++的)来分配内存,然后在该内存块中调用类的构造函数来构造对象。   同理,dlete来释放类对象时,先调用类的析构函数析构对象,然后调用delete操作符函数(如new同理)释放对象的内存块。

从以上过程可以看到,可以通过在类中重载new 和delete操作符函数来控制类对象在特定的进程堆中创建。见代码:

class A{ private:  static HANDLE s_hHeap;//堆句柄  static UINT  s_nNumAllocsInHeap ;//计数器,堆中分配的对象计数  // 声明为静态成员,使得类的所有实例对象都分配在同一堆内存中,使用同一个计数器。 public:    A(){};//默认构造函数void* operator new(size_t size);//分配内存空间void  operator delete(void* p);//释放内存空间}//初始化静态成员HANDLE A::s_hHeap=NULL;UINT  A::s_nNumAllocsInHeap=0;void* A::operator new(size_t size){if(s_hHeap==NULL){//第一次创建对象时需要创建堆 s_hHeap=HeapCreate(HEAP_NO_SERIALITE,0,0); if(s_hHeap==NULL)  return(NULL);}void* p=HeapAlloc(s_hHeap,0,size);//从堆中分配内存块if(s_hHeap!=NULL)  s_nNumAllocsInHeap++;return(p);//返回堆中分配内存块指针  }void A::operator delete(void* p){if(HeapFree(s_hHeap,0,p);//释放内存块 s_nNumAllocsInHeap--;if(s_nNumAllocsInHeap==0){//堆中所有对象都被释放则释放堆 if(HeapDestory(s_hHeap)//销毁堆   s_hHeap=NULL;//下次创建类对象时需要重新创建堆 }}
不为每个对象创建堆的唯一好处在于不会有额外的性能和内存开销,但这些额外开销并不大,与潜在的回报相比,这些很可能是值得的。






0 0