C ++ memory management 4-Windows Heap Manager programming (rpm)

 

1 Introduction

  In most Windows applications design, almost inevitable to memory operation and management. In the dynamic allocation of memory large size especially noticeable when important. This paper mainly heap memory management management techniques are discussed.

  Heap (Heap) is actually located in the reserved virtual address space of a region . In the beginning, most of the pages in the holding area has not been submitted to physical memory. As the memory allocated from the heap more and more, the heap manager will gradually put more physical memory presented to the heap. Physical memory allocated from the heap of pages of documents in the system, a dedicated heap manager is responsible for the recovery of occupied physical memory at the time of release. Management is also a heap memory management provided by Windows. Primarily for dispensing small data blocks . And the other two Windows memory management of virtual memory (1) and (2) memory mapped files compared heap do not have to consider such as the system of allocation granularity and page borders like more cumbersome and easy to ignore the problem, attention can be force focused on the design features of the program code. But the use of the heap to allocate and release memory speed is much slower than the other two mechanisms , but does not have direct control of the physical memory and the ability to submit recovery .

  At the time the process has just started , the system will be in the newly created virtual address space of the process in creating a heap , the default process heap the heap is the default size is 1MB, this value is allowed to change (that is, when the link program, VS compiler when, in the project settings - connection settings - can be set) . The default process heap is more important, for a number of Windows functions use . In use, the system must be guaranteed within the specified time, this is only one thread can each allocation and deallocation default heap memory block. Although such restrictions will have some impact on access speed, but it can ensure sequential access to the default stack multiple threads in the process of calling various Windows functions simultaneously .

  It allows the use of multiple stacks in the process, including the process default heap, including each stack has a heap handle to identify . And create their own stack of different processes created by default heap, destroyed by the system to complete, and its lifetime as early as before the start of the implementation process has already begun , although you can () function to get the process by default heap handle GetProcessHeap program but it was not allowed to call HeapDestroy () function explicitly revoke it .

 

2. The need for dynamic creation of the heap

  As we noted earlier, in addition to the process of the process default heap , you can also in the process virtual address space in a dynamically created some independent stack . As to whether the program design or need to dynamically create a separate heap from , whether it can more efficiently manage memory if there is need to protect the components, whether there is a need for local access, and whether there is a need to reduce overhead and thread synchronization is there a need for the prompt release of several reactors and other aspects to consider.

  (1) whether there is protection components of this principle requires relatively easy to understand. In FIG. 1, a left side view showing a list (node structure) and a tree component (branched structure) the use of a component stack together. In this case, since the data stored in the two component mixing stack, if node 3 after a few bytes (of list component) is erroneously rewritten due, it will likely affect the subsequent 2 to the branch located (belonging to the tree components). This will cause the relevant components of the tree can not be performed due to the memory corruption when traversing its tree. The reason is due to memory tree component list established its own operating error caused. If shown on the right, and the tree component assembly lists are stored in a separate stack, the above-described situation is clearly not occur, the error will be limited to the chain assembly was erroneous operation, and since stored in a separate tree component heap and protected.

  【figure 1】

  In the figure, each node in the list if the component occupies 12 bytes, occupies 16 bytes of each branch of the tree if these component objects of different lengths share a stack (left), these have been left in allocated memory heap filled objects, which if nodes 2 and 4 release, will have 24 bytes of fragments, if the attempts to allocate a 16-byte within 24 byte branch target idle interval , although the number of bytes to be allocated is smaller than the number of idle bytes, but still fail allocation. Only assign the same size of the object on the stack before they can implement a more efficient memory management. If the tree is replaced by other components of the assembly length of 12 bytes, then after releasing an object, another object can be filled with exactly this newly freed space objects.

  (2) requires local access is a more important principle. The system will often be a page swap between memory and system page file, but if the exchange too many times to run the system performance will be greatly affected . Therefore, in the program should be designed to avoid frequent switching system page, if those data are accessed simultaneously assigned to a position close to each other, the system will reduce the pages between memory and paging file exchange frequency.

  (3 ) thread synchronization overhead refers to a default condition heap run in a sequential manner to protect data while trying to access from destruction must perform extra code overhead spent in multiple threads. This overhead to ensure the safety of the thread stack, and therefore it is necessary, but for a large number of heap allocation operations, such additional costs would be a burden, and reduce operating performance of the program . To avoid this extra cost, you can notify the system has only a single thread to access when creating a new heap . At this heap of security thread applications will have to be responsible.

  (4) Finally , if there is need to quickly release the stack , it can be dedicated stack for some of the data structures, and to release the entire stack, Not to explicitly free memory in the heap of blocks each. For most applications, this process will run faster .

 

3.   Create a heap

  In the process, if necessary, dynamically created based on the original default heap on a pile, by HeapCreate () function to complete:

HANDLE HeapCreate(
 DWORD flOptions,
 DWORD dwInitialSize,
 DWORD dwMaximumSize
);

 

  Its first argument flOptions attribute specifies the operation of the new stack . This flag will affect some of the heap functions such as HeapAlloc (), HeapFree (), HeapReAlloc () and HeapSize () such as access to the new heap. Possible values and combinations of the following flags:

 

 

 

  参数dwInitialSize和dwMaximumSize分别为堆的初始大小和堆栈的最大尺寸。其中,dwInitialSize的值决定了最初提交给堆的字节数。如果设置的数值不是页面大小的整数倍,则将被圆整(Round Up)到邻近的页边界处。dwMaximumSize则实际上是系统能为堆保留的地址空间区域的最大字节数。如果该值为0,那么将创建一个可扩展的堆,堆的大小仅受可用内存的限制如果应用程序需要分配大的内存块,通常要将该参数设置为0。如果dwMaximumSize大于0,则该值限定了堆所能创建的最大值,HeapCreate()同样也要将该值圆整到邻近的页边界,然后再在进程的虚拟地址空间堆保留该大小的一块区域。在这种堆中分配的内存块大小不能超过0x7FFF8字节,任何试图分配更大内存块的行为将会失败,即使是设置的堆大小足以容纳该内存块

  如果HeapCreate()成功执行,将会返回一个标识新堆的句柄,并可供其他堆函数使用

  需要特别说明的是,在设置第一个参数时,对HEAP_NO_SERIALIZE的标志的使用要谨慎,一般应避免使用该标志。这是同后续将要进行的堆函数HeapAlloc()的执行过程有关系的,在HeapAlloc()试图从堆中分配一个内存块时,将执行下述几步操作:

  1) 遍历分配的和释放的内存块的链接表

  2) 搜寻一个空闲内存块的地址

  3) 通过将空闲内存块标记为"已分配"来分配新内存块

  4) 将新分配的内存块添加到内存块列表

  果这时有两个线程1、2试图同时从一个堆中分配内存块,那么线程1在执行了上面的1和2步后将得到空间内存块的地址。但是由于CPU对线程运行时间的分片,使得线程1在执行第3步操作前有可能被线程2抢走执行权并有机会去执行同样的1、2步操作,而且由于先执行的线程1并没有执行到第3步,因此线程2会搜寻到同一个空闲内存块的地址,并将其标记为已分配而线程1在恢复运行后并不能知晓该内存块已被线程2标记过,因此会出现两个线程军认为其分配的是空闲的内存块,并更新各自的联接表。显然,象这种两个线程拥有完全相同内存块地址的错误是非常严重而又是难以发现的。

  由于只有在多个线程同时进行操作时才有可能出现上述问题,一种简单的解决的办法就是不使用HEAP_NO_SERIALIZE标志而只允许单个线程独占地对堆及其联接表拥有访问权。如果一定要使用此标志,为了安全起见,必须确保进程为单线程的或是在进程中使用了多线程,但只有单个线程对堆进行访问。再就是使用了多线程,也有多个线程对堆进行了访问,但这些线程通过使用某种线程同步手段。如果可以确保以上几条中的一条成立,也是可以安全使用HEAP_NO_SERIALIZE标志的,而且还将拥有快的访问速度。如果不能肯定上述条件是否满足,建议不使用此标志而以顺序的方式访问堆,虽然线程速度会因此而下降但却可以确保堆及其中数据的不被破坏。

 

4. 从堆中分配内存块

  在成功创建一个堆后,可以调用HeapAlloc()函数从堆中分配内存块

  在此,该函数可以从两个种堆中分配内存块。(1)从用HeapCreate()创建的动态堆中分配内存块,(2)也可以直接从进程的默认堆中分配内存块。

  下面先给出HeapCreate()的函数原型

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

 

  其中,参数hHeap为要分配的内存块来自的堆的句柄(从分配的哪个堆中进行分内存块),可以是从HeapCreate()创建的动态堆句柄也可以是由GetProcessHeap()得到的默认堆句柄。

  参数dwFlags指定了影响堆分配的各个标志。该标志将覆盖在调用HeapCreate()时所指定的相应标志,可能的取值为:

 

 

 

  最后一个参数dwBytes设定了要从堆中分配的内存块的大小。如果HeapAlloc()执行成功,将会返回从堆中分配的内存块的地址。如果由于内存不足或是其他一些原因而引起HeapAlloc()函数的执行失败,将会引发异常。通过异常标志可以得到引起内存分配失败的原因:如果为STATUS_NO_MEMORY则表明是由于内存不足引起的;如果是STATUS_Access_VIOLATION则表示是由于堆被破坏或函数参数不正确而引起分配内存块的尝试失败。以上异常只有在指定了HEAP_GENERATE_EXCEPTIONS标志时才会发生如果没有指定此标志,在出现类似错误时HeapAlloc()函数只是简单的返回NULL指针

  在设置dwFlags参数时,如果先前用HeapCreate()创建堆时曾指定过HEAP_GENERATE_EXCEPTIONS标志,就不必再去设置HEAP_GENERATE_EXCEPTIONS标志了,因为HEAP_GENERATE_EXCEPTIONS标志已经通知堆在不能分配内存块时将会引发异常。另外,对HEAP_NO_SERIALIZE标志的设置应慎重,与在HeapCreate()函数中使用HEAP_NO_SERIALIZE标志类似,如果在同一时间有其他线程使用同一个堆,那么该堆将会被破坏。如果是在进程默认堆中进行内存块的分配则要绝对禁用此标志。

  在使用堆函数HeapAlloc()时要注意:堆在内存管理中的使用主要是用来分配一些较小的数据块,如果要分配的内存块在1MB左右,那么就不要再使用堆来管理内存了,而应选择虚拟内存的内存管理机制。

 

5. 再分配内存块

  在程序设计时经常会由于开始时预见不足而造成在堆中分配的内存块大小的不合适(多数情况是开始时分配的内存较小,而后来实际需要更多的数据复制到内存块中去)这就需要在分配了内存块后再根据需要调整其大小。堆函数HeapReAlloc()将完成这一功能,其函数原型为:

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

参考:

  https://www.cnblogs.com/findumars/p/5929832.html

Guess you like

Origin www.cnblogs.com/icmzn/p/11823744.html