Spatial configurator for C++ STL

Introduction to Space Configurator

We know that C++ memory configuration operations and release operations are like this:

class Foo {
    
    ...};
Foo* pf = new Foo;	//配置内存,然后构造对象
delete pf;			//将对象析构,然后释放内存

new includes two phases of operation: 1. Call operator new to configure memory. 2. Call the constructor to construct the object content.

delete also contains two phases of operation: 1. Call the destructor. 2. Call operator delete to release memory.

For precise division of labor, STL separates these two phases of operations. Member function allocate() is responsible for memory configuration operation, dealcate() is responsible for memory release; construct() is responsible for object construction, and destroy() is responsible for object destruction .

In the process of memory allocation, there are several issues to consider:

  1. Memory fragmentation problems caused by small blocks of memory.
  2. Performance problems caused by frequent application and release of small blocks of memory.

In order to solve these problems, SGI STL designed a two-level configurator, that is, the first-level configurator and the second-level configurator. The first-level allocator uses malloc() and free() directly. The second-level configurator adopts different strategies depending on the situation: when the configuration block exceeds 128 bytes, it is deemed to be "big enough", and the first-level configurator is called ; when the configuration block is smaller than 128 bytes, it is regarded as "Too small", in order to reduce the additional burden, a complex memory pool management method is adopted .

First level configurator

insert image description here
SGI's first-level configurator uses C functions such as malloc(), free(), and realloc() to perform actual memory configuration, release, and reconfiguration operations. When malloc or realloc fails, call oom_malloc() and oom_realloc() instead. The latter two have inner loops, which constantly call out-of-memory processing routines, expecting to obtain enough memory after a certain call. But if the out-of-memory processing routine is not set by the client, a bad_alloc exception will be thrown directly, or the program will be terminated .

Note: It is the client's responsibility to design the out-of-memory handling routine, and it is also the client's responsibility to set the out-of-memory handling routine.

Second level configurator

The secondary configurator uses the form of memory pool + free linked list to avoid fragmentation caused by small blocks of memory, improve the efficiency of allocation, and improve the utilization rate. It is managed by a 16-element free linked list (free_list), and the memory size of each location is a multiple of 8, which are: 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128.

The node structure of free_list is as follows, the use of union is to save memory, so that each node does not need additional pointers:

union obj
{
    
    
	union obj* free_list_link;
	char client_data[1];
}

The relationship between memory pool and free linked list

The relationship between the memory pool and the free linked list free_list is shown in the following figure:
insert image description here

Among them, the first element of free_list points to 8 bytes of space, and I allocated 10 of 8 bytes of space. The last element of free_list points to a space of 128 bytes, and I allocated 4 of this space.

free_list manages the unused memory that has been allocated to free_list in the memory pool. If the system wants to take an 8-byte memory from free_list, it will directly pop the first element at the top from free_list[0], and then move the top back.

Secondary configurator memory allocation

There are mainly four situations:

  1. There is free memory in the free_list list. If you apply for 3 bytes of memory, the required space size is increased to a multiple of 8, and then go to free_list to find the corresponding linked list, if free_list[i] is not empty, return the first element, and then move the head pointer backward shift.
  2. There are no frees in the free_list list, but the mempool is not empty. First check whether the size in the memory pool is larger than the requested memory, for example, apply for 20*8 memory, if it is enough, allocate the corresponding memory, allocate one of them to the user, and hang the others in the corresponding free_list . If the memory pool is not large enough and only enough for a few memory allocations, then allocate these and return the corresponding data. If even one is not enough, the third case is executed.
  3. There is no space in the free_list list, and the memory pool is not enough. Call malloc to re-allocate memory. When allocating, it will allocate twice as much memory , hang the corresponding memory under free_list, and put the rest in the memory pool.
  4. There is no space in the free_list list, the memory pool is not enough, and malloc fails. Then call the first-level space configurator, which will have loop processing or throw an exception.

When the memory requested by the user from the secondary space configurator is released, the secondary space configurator inserts the reclaimed memory into the corresponding free_list instead of releasing this memory directly . The process is as follows:
insert image description here

Summarize

The implementation idea of ​​the first-level configurator is actually to encapsulate malloc, relloc, and free under c, and add c++ exceptions to it. We need to understand that when the out-of-memory call fails, the out-of-memory handling is a problem that users need to solve. STL does not deal with it, but throws an exception when you have no out-of-memory handling method or have but the call fails.

The second-level configurator is a particularly delicate idea that combines the memory pool and the free list.
Each node of the free list maintains the memory size in multiples of 8 (8, 16, 32...128 bytes). First, the user applies for a memory of less than 128 bytes and enters the second-level configurator program. If the user applies for a memory of 32 bytes for the first time, the program directly applies for 40 32-byte spaces, one for the customer, and the other 19 Hand over to the free list, and the remaining 20 to the memory pool . Next, apply for space for the second time. Assume that the user needs 64 bytes of space this time. First, the program checks whether the 64-byte free linked list node has free space. If there is, it will be allocated directly. If not, it will go to the memory pool. Then check how many 64-byte nodes can be allocated by the size of the memory pool. If it is fully satisfied, one will be given to the user, and the remaining 19 will be given to the free list. If the remaining space is not enough, 20 64-byte nodes will be allocated. , allocate the maximum number of memory pools to users and free lists as much as possible. If none of them can be allocated, first allocate the remaining useless small memory in the memory pool to the corresponding free linked list, and then continue to call S_chunk_alloc to apply for memory. If the memory application is insufficient, check whether there is any unused large one in the free linked list If there is a memory block, it will be taken out to the memory pool, and S_chunk_alloc will be called recursively. If the free list is gone, it will be handed over to the first-level adapter (because it has a method for dealing with insufficient memory). If the memory is sufficient, the requested memory is obtained to supplement the memory pool. Repeat the above operation, give one to the client, the other 19 to the free list, and the rest to the memory pool.

Guess you like

Origin blog.csdn.net/TABE_/article/details/126527159