5. Memory Management Mechanism-heap)
· Application scenarios
A heap is a region created in the process space when a process is created. It is managed by the heap manager. A process can have many heaps. A process has a default heap of 1 MB, which can be dynamically expanded.
When the program needs to manage many small objects, it is suitable to use the heap; when the required space is larger than 1 MB, it is best to use virtual memory for management.
The advantage of heap is that there is a heap manager to manage it, and you do not need to manage specific things such as page boundaries and allocation granularity. You can see from calling functions, there are a lot fewer parameters than virtualalloc.
The disadvantage of the heap is that the allocation and release speed is slower than the previous several mechanisms, so it is best not to exceed 1 MB; unlike the virtual memory, it can be submitted and released at any time, because it is determined by the heap manager. If heap is used to allocate 1 GB of space, one Shard is required, and virtual memory is used, there is no latency.
· Default heap
By default, the process heap is used by all threads. The system synchronizes the heap whenever the thread needs to allocate and release the memory zone from the heap, so the access speed is slow.
Its default size is 1 M. Similarly, you can use the following link command to change its size:
# Pragma comment (linker, "/heap: 102400000,1024000 ")
The first value is the heap reserved space, and the second value is the physical memory size submitted at the beginning of the heap. This article changes the heap to 100 MB.
When you increase the physical memory submitted by the heap in the program, the physical memory will decrease when the process is running. However, the default heap can always be expanded and its maximum value cannot be limited.
When you expand the heap reserved space in the program, the available process space will decrease when the process is running.
Every time you use the new operator to allocate memory, the process space is reduced accordingly, and the physical memory is also reduced accordingly.
An Important Note: after testing, if most of the memory blocks you need exceed 512 KB, the initial size of the heap should not be large because, if the required memory block is greater than K, it is not allocated from the heap, that is, it does not use the default space in the heap, but it is still managed by the heap.
One use of the default heap is that system functions need to use it to run. For example, the character set of Windows2000 is Unicode. If you call functions of the ANSI version, the system must use the heap to convert functions of the ANSI version to Unicode version and call functions of the Unicode version.
· Self-built heap
Usage
Data Structure protection:
Different data structures are stored in different heaps to prevent them from being damaged by pointer misoperations between different structures.
Eliminate memory fragments:
Storing structures of different sizes in a heap will lead to the generation of fragments. For example, when a small structure is released, the large structure cannot be used.
Exclusive heap speed:
If the default heap is used, synchronous access is performed between threads, which is slow. If an exclusive heap is created, the system does not need to synchronize data, which is faster.
The second is the release speed. In the default heap, you can only release a memory block but not the entire heap. The exclusive heap can release the heap at a time, that is, all memory blocks are released.
Start to use
Create a heap:
Use the following APIs
Handle heapcreate (DWORD option, size_t initial size, maximum size_t)
The value of "option" is 0, not any of the following
Heap_no_serialize. The system does not need to synchronize the heap.
Heap_generate_exceptions: An exception occurs when the creation fails or the allocation fails.
The "initial size" is the heap size. The system will normalize the page to an integer multiple times, for example, 0 ~ Any number of 4096 is 4096; however, the process space must be at least 64 KB.
"Maximum" is the maximum value allowed by the heap; if it is 0, it is unlimited.
To use heap_no_serialize, make sure that only a single thread can access the heap. Otherwise, the heap may be damaged. Or the program may have Synchronous Code to synchronize the heap.
The C ++ program is as follows:
Pheap = (char *) getprocessheap ();
Printf ("Default heap address = % x/N", pheap );
Memorystatus memstatus2;
Globalmemorystatus (& memstatus2 );
Handle hheap = heapcreate (heap_no_serialize | heap_generate_exceptions, 1024*1024*50, 0 );
Char * pheap = (char *) hheap;
Printf ("new heap 1 address = % x/N", pheap );
If (hheap = NULL)
{
Cout <"failed to create heap! "<Endl;
}
Memorystatus memstatus3;
Globalmemorystatus (& memstatus3 );
Cout <"after the heap is created:" <Endl;
Cout <"reduce physical memory =" <memstatus2.dwavailphys-memstatus3.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus2.dwavailpagefile-memstatus3.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus2.dwavailvirtual-memstatus3.dwavailvirtual <Endl;
Handle hheap2 = heapcreate (heap_no_serialize | heap_generate_exceptions, 1024*1024*10, 0 );
Char * pheap2 = (char *) hheap2;
Printf ("new heap 2 address = % x/N", pheap2 );
The result is as follows:
When heap 1 is created, it allocates 50 MB of physical memory for the heap. When heap 2 is created, the address of heap 2 is 0x04bc 0000 = 0x019c 0000 + 50*1024*1024.
Memory Allocation:
Use the following APIs
Pvoid heapalloc (handle heap handle, DWORD option, size_t bytes)
"Option" can be,
Heap_zero_memory, all bytes initialized to 0
Heap_no_serialize, exclusive to the heap memory Zone
Heap_generate_exceptions. If the heap is created, you do not need to set it again. The exception may be status_no_memor (with insufficient memory) and status_access_violation (heap destroyed, allocation failed ).
The C ++ program is as follows:
Globalmemorystatus (& memstatus3 );
Pvoid Pv = heapalloc (hheap,
Heap_zero_memory | heap_no_serialize | heap_generate_exceptions, 1024*507 );
If (Pv = NULL)
{
Cout <"heap memory allocation failed! "<Endl;
}
Char * Pc = (char *) PV;
Printf ("first allocated address = % x/N", PC );
Memorystatus memstatus4;
Globalmemorystatus (& memstatus4 );
Cout <"after the first heap allocation:" <Endl;
Cout <"reduce physical memory =" <memstatus3.dwavailphys-memstatus4.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus3.dwavailpagefile-memstatus4.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus3.dwavailvirtual-memstatus4.dwavailvirtual <Endl;
Pvoid pv2 = heapalloc (hheap, heap_zero_memory | heap_no_serialize | heap_generate_exceptions, 1024*508 );
If (pv2 = NULL)
{
Cout <"heap memory allocation failed! "<Endl;
}
Char * PC2 = (char *) pv2;
Printf ("second allocation address = % x/N", PC2 );
Memorystatus memstatus5;
Globalmemorystatus (& memstatus5 );
Cout <"after the second heap allocation:" <Endl;
Cout <"reduce physical memory =" <memstatus4.dwavailphys-memstatus5.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus4.dwavailpagefile-memstatus5.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus4.dwavailvirtual-memstatus5.dwavailvirtual <Endl;
For (INT I = 0; I <200*1024; I ++)
PC2 [I] = 9;
Memorystatus memstatus10;
Globalmemorystatus (& memstatus10 );
Cout <"after half of the second heap usage:" <Endl;
Cout <"reduce physical memory =" <memstatus5.dwavailphys-memstatus10.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus5.dwavailpagefile-memstatus10.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus5.dwavailvirtual-memstatus10.dwavailvirtual <Endl;
The result is as follows:
It can be seen that the address allocated for the first time is 0x04ad d650 <0x04bc 0000, which is allocated in the heap; the address allocated for the second time is 0x055c 0020> 0x04bc 0000, it is allocated out-of-heap. No matter how large the heap is, it will be allocated out-of-heap as long as the allocated memory block is greater than K. However, it is like in the heap, exist in the heap chain table and managed by the heap. During allocation, the system uses a virtual page file. Physical memory is allocated only when it is actually used.
It is still unclear why the heap memory will be allocated outside the heap instead of being used directly when the allocation is greater than K.
Change size:
Pvoid heaprealloc (handle heap handle, DWORD option, pvoid old memory block address, size_t new memory block size)
In addition to the preceding three options, "option" also includes heap_realloc_in_place_only, which specifies that the address of the original memory block cannot be moved.
The C ++ program is as follows:
Globalmemorystatus (& memstatus4 );
Pvoid pv2new = heaprealloc (hheap, 0, pv2, 1024*1024*2 );
If (pv2new! = NULL)
{
Char * pc2new = (char *) pv2new;
Printf ("Change Allocation address = % x/N", pc2new );
Cout <pc2new [0] <Endl;
// Cout <PC2 [0] <Endl; access violation
Size_t lennew = heapsize (hheap, 0, pv2new );
Cout <"changed size =" <lennew <Endl;
}
Globalmemorystatus (& memstatus5 );
Cout <"after changing the allocation:" <Endl;
Cout <"reduce physical memory =" <memstatus4.dwavailphys-memstatus5.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus4.dwavailpagefile-memstatus5.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus4.dwavailvirtual-memstatus5.dwavailvirtual <Endl;
The result is as follows:
It can be seen that the new memory block is created immediately after the original memory block ends. The size is 2 MB. The content of the original memory block is destroyed and released, therefore, the new memory block only reduces the amount of memory added. One disadvantage is that the new memory block does not retain the original memory content! In addition, if heap_realloc_in_place_only is used, the not enough quote exception occurs. That is to say, the current memory condition is that the memory block can be expanded only when it is moved.
Query memory:
You can query the size of a memory block in the heap.
Size_t heapsize (handle heap handle, DWORD option, lpvoid memory block address)
"Option" can be 0 or heap_no_serialize.
Refer to the above example.
Release memory blocks:
Bool heapfree (handle heap handle, DWORD option, pvoid memory block address)
"Option" can be 0 or heap_no_serialize.
The C ++ program is as follows:
Globalmemorystatus (& memstatus5 );
Heapfree (hheap, 0, pv2new );
Memorystatus memstatus6;
Globalmemorystatus (& memstatus6 );
Cout <"after the second heap allocation is released:" <Endl;
Cout <"added physical memory =" <memstatus6.dwavailphys-memstatus5.dwavailphys <Endl;
Cout <"add Available page file =" <memstatus6.dwavailpagefile-memstatus5.dwavailpagefile <Endl;
Cout <"add available process space =" <memstatus6.dwavailvirtual-memstatus5.dwavailvirtual <Endl;
The result is as follows:
The memory space has released the original 2 MB space.
Release heap:
Bool heapdestroy (handle heap handle)
You cannot use it to release the default heap. The system ignores its processing.
This time, we first allocated 70 MB of memory in heap 1, because it is very large, so the heap allocated memory to it outside the heap, so, heap 1 has a total of 50 m + 70 m = 120 m. The release procedure is as follows:
Pvoid PV4 = heapalloc (hheap, heap_zero_memory | heap_no_serialize | heap_generate_exceptions), 1024*1024*70 );
If (PV4 = NULL)
{
Cout <"heap memory allocation failed! "<Endl;
}
Char * pc4 = (char *) PV4;
Printf ("fourth heap allocation = % x/N", pc4 );
Memorystatus memstatus9;
Globalmemorystatus (& memstatus9 );
Cout <"after heap memory allocation:" <Endl;
Cout <"reduce physical memory =" <memstatus7.dwavailphys-memstatus9.dwavailphys <Endl;
Cout <"reduce available page files =" <memstatus7.dwavailpagefile-memstatus9.dwavailpagefile <Endl;
Cout <"reduce available process space =" <memstatus7.dwavailvirtual-memstatus9.dwavailvirtual <Endl;
Size_t Len = heapsize (hheap, 0, PV4 );
Cout <"Len =" <Len <Endl;
Bool Re = heapdestroy (hheap );
If (RE = false)
{
Cout <"failed to release heap! "<Endl;
}
Memorystatus memstatus8;
Globalmemorystatus (& memstatus8 );
Cout <"after the heap is released:" <Endl;
Cout <"added physical memory =" <memstatus8.dwavailphys-memstatus9.dwavailphys <Endl;
Cout <"add Available page file =" <memstatus8.dwavailpagefile-memstatus9.dwavailpagefile <Endl;
Cout <"add available process space =" <memstatus8.dwavailvirtual-memstatus9.dwavailvirtual <Endl;
The result is as follows:
As I guess, MB of memory is released.
Get all heaps:
DWORD getprocessheaps (DWORD quantity, phandle handle array)
"Quantity" is the number of heaps you want to obtain;
The "handle array" is the obtained heap handle.
The default heap can also be obtained.
Handle handles [10];
Memset (handles, 0, sizeof (handles ));
Getprocessheaps (10, handles );
For (INT I = 0; I <10; I ++)
Cout <"Heap" <I + 1 <"=" The result is as follows:
As you can see, there are a total of 8 heaps. Heap 1 is the default heap, and heap 7 and heap 8 are the heap created in this article. The other five do not know the source.
Verification heap:
Bool heapvalidate (handle heap handle, DWORD option, lpvoid memory block address)
"Option" can be 0 or heap_no_serialize;
When "memory block address" is null, all memory blocks are verified.
The C ++ program is as follows:
Handle handles [10];
Memset (handles, 0, sizeof (handles ));
Getprocessheaps (10, handles );
For (INT I = 0; I <10; I ++)
{
Cout <"Heap" <I + 1 <"=" If (heapvalidate (handles [I], 0, null ))
Cout <"the heap is verified successfully! "<Endl;
Else
Cout <Endl;
}
The result is as follows:
Merge memory blocks:
Uint heapcompact (handle heap handle, DWORD option)
"Option" can be 0 or heap_no_serialize;
This function can merge idle memory blocks.
Other functions:
Heaplock and heapunlock are usually used by the system;
Heapwalk can traverse the heap memory. The above two functions are required.
· C ++ memory functions
Malloc and free are functions used in C language. Memory can only be allocated from the default heap, but only memory can be allocated. constructors cannot be called, only allocated by byte, and not by type.
New and delete are functions used in the C ++ language. By default, memory is allocated from the default heap. However, you can also use the new function to allocate memory by type from the self-built heap; you can also execute constructor and destructor. The underlying layer is implemented through heapalloc and heapfree. Depends on the implementation of the compiler.
Globalalloc and globalfree are functions that are slower than heapalloc and heapfree, but they do not have better advantages. They can only be allocated in the default heap; they are used to allocate memory in a 16-bit operating system.
Localalloc and localfree
In the WindowsNT kernel, it is the same as globalalloc and globalfree.
· An Example
By default, the New Keyword uses heapalloc to create an object on the default stack. This article reloads the new method of the class so that the class is stored in its own heap, which can be isolated from the external object to avoid unexpected damage to the important data structure. Because the member variables in the class are stored in the heap, they are not limited to 1 m space of the thread stack.
The C ++ program is as follows:
Class allocateinotherheap
{
Public:
Allocateinotherheap (void );
~ Allocateinotherheap (void );
Void * operator new (size_t size );
Static handle heap;
Public:
// The space required for the unique Class Object
Int iarray [1024*1024*10];
Allocateinotherheap: allocateinotherheap (void)
{
Cout <"allocateinotherheap ()" <Endl;
// If the new function does not allocate enough space, access violations will occur here
Memset (iarray, 0, sizeof (allocateinotherheap ));
Iarray [1024] = 8;
}
Void * allocateinotherheap: Operator new (size_t size)
{
If (heap = NULL)
Heap = heapcreate (heap_no_serialize | heap_generate_exceptions, 1024*1024*10, 0 );
// Allocate enough space for this Class Object
Void * P = heapalloc (heap, 0, sizeof (allocateinotherheap ));
Cout <"heap size =" Printf ("allocateinotherheap heap address = % x/N", heap );
Printf ("allocateinotherheap return address = % x/N", P );
Return P;
}
Allocateinotherheap ::~ Allocateinotherheap (void)
{
Cout <"~ Allocateinotherheap "<Endl;
}
Void allocateinotherheap: Operator Delete (void * P)
{
Heapfree (heap, 0, P );
Heapdestroy (HEAP );
Cout <"Delete ()" <Endl;
}
};
The result is as follows:
It can be seen that the new function allocates enough space before initializing the object variable. The delete function must first analyze the structure to release the space. The object is stored outside the heap because it is greater than 512 K. The object size is exactly the size of the iarray variable.
Note: If you do not allocate enough space, although you can get the object pointer, access violations may occur when you access the data. If you do not, it will be worse, it means you read and write data from others.