STL source code profiling-space Configurator

Source: Internet
Author: User

I have read the source code of the STL space configurator and summarized it as follows:
1. STL space configurator: It mainly consists of three file implementations. stl_construct.h defines the global functions construct () and destroy (), which are responsible for Object Construction and Analysis. The stl_alloc.h file defines level 1 and level 2 provisors. The provisioner name is alloc. stl_uninitialized.h defines some global functions to fill (fill) or copy (copy) large memory data. They are also part of the STL standard plan.
Two levels of configurator are defined in stl_alloc.h. The main idea is to apply for a large memory pool. The small memory pool applies directly from the memory pool. When not enough, apply for a new memory pool, in addition, you can directly apply for large memory blocks. When the requested space is larger than 128 bytes, the first-level Configurator is called. The first-level configurator does not use OPERATOR: New or operator: Delete to apply for space, instead, it calls malloc/free and realloc directly, and implements a mechanism similar to new-handler in C ++. The so-called C ++ new handler mechanism is that you can require the system to call a specified function when the memory configuration requirements cannot be met. In other words, once: Operator: New cannot complete the task, the processing routine specified by the client is called before the STD: bad_alloc exception state is thrown, this processing routine is often called a new-handler.new-handler to address memory practices with a specific pattern. Both allocate () and realloc of the first-level SGI configurator call oom_malloc () and oom_realloc () after malloc and realloc are unsuccessful. Both of them have an internal loop, the "memory insufficiency processing routine" is continuously called. It is expected that the task will be completed successfully after a certain call with sufficient memory. However, if the "memory-insufficient processing routine" is not set by the client, oom_malloc () and oom_realloc call _ throw_bad_alloc,
Throw the bad_alloc exception information, or use exit (1) to stop the program.
In the second-level configurator defined in stl_alloc.h, if the block size is large enough to exceed 128 bytes, it is handed over to the first-level configurator for processing. When the block size is less than 128 bytes, it is managed by the memory pool. This method is also called the sub-layer configuration. Each time a large block of memory is configured, the corresponding free-list is maintained ). If you need memory of the same size again next time, you can pull it out from the free-List directly. If the customer returns a small block, it will be recycled from the provisioner to free-lists. In addition, the provisioner is also responsible for configuration and collection. For ease of management, the second-level SGI configurator will take the initiative to increase the memory demand for any small block to a multiple of 8. 16 free-lists are maintained and their respective management sizes are 8, 16, 24, 32, 40, 48, 56, 64, 104, 80,
A small block of 112,120,128 bytes. When the request is less than or equal to 128 bytes, the corresponding free list will be checked. If the free-list contains available blocks, it will be taken directly. If not, you are ready to refill the space for the corresponding free-list. The new space will be taken from the memory pool. By default, 20 new nodes are obtained. If the memory pool is insufficient (more than one node), the corresponding number of nodes will be returned. if the size of a node in the memory pool is not enough, apply for a new memory pool. The size is 2 * total_bytes + round_up (heap_size> 4), and totoal_bytes is the size of the applied space, round_up is adjusted to a multiple of 8, and heap_size is the size of the current total applied memory pool. If the application for this memory pool is successful, the remaining space in the original memory pool will be allocated to the appropriate free-list. In case of exhaustion, the entire system
If the heap space is insufficient (or the source memory cannot be injected into the memory pool) and the malloc () action fails, the system will look for free lists with "unused blocks, and the blocks are large enough. if you find it, you can dig and hand it over. If you cannot find it, you can call the first-level configurator. The first-level configurator uses malloc to configure memory. However, it has an out-of-Memory Processing Mechanism (similar to the new-handler mechanism), and may have the opportunity to release other memory for use here. If yes, the operation succeeds. Otherwise, an exception occurs in bad_alloc.
2. Default memory distributor of STL
Memory Management after these containers is implemented through a default Allocator provided by STL. Of course, you can also customize your Own Allocator by implementing the interface method defined in the Allocator template, and then passing the custom Allocator as a template parameter to the STL container, create an STL container object that uses custom Allocator, for example:
STL: vector <int, userdefinedallocator> array;
In most cases, the default STL allocator is enough. This allocator is a memory manager composed of two-level distributors. When the applied memory size is greater than bytes, the first-level distributor is started to directly allocate the heap space to the system through malloc, if the requested memory size is less than bytes, the second-level distributor is started to deliver a piece of memory from a pre-allocated memory pool to the user, the memory pool consists of 16 different sizes (multiples of 8, 8 ~ 128 byte). Allocator extracts the header block from the corresponding idle block list based on the Applied memory size (which is a multiple of 8.
This method has two advantages:
(1) Fast allocation of small objects. Small objects are allocated from the memory pool. This memory pool is used by the system to allocate a large enough area to the program for backup once malloc is called, when the memory pool is exhausted, apply for a new area from the system. The whole process is similar to wholesale and retail. Allocator first wholesalers a certain amount of goods to the general business, and then retail it to the user, it is faster than the process in which a commodity is always sold to the user. Of course, when there is a problem here, the memory pool will cause some memory waste. For example, if you only need to allocate a small object, you may need to apply for a large memory pool for this small object, however, this waste is worthwhile. Moreover, this situation is rare in practical applications.
(2) avoid the generation of memory fragments. The allocation of small objects in the program can easily cause memory fragmentation, which puts a lot of pressure on the memory management of the operating system. The increase of fragments in the system will not only affect the speed of memory allocation, it also greatly reduces the memory usage. The memory pool organizes the memory of small objects. From the system perspective, it is only a large memory pool, and the allocation and release of small object memory cannot be seen.
During implementation, Allocator needs to maintain an array free_list that stores 16 idle Block List headers. The array element I is an array with a size of 8 * (I + 1) pointing to the block) header of the byte free Block List, a pointer to the start address of the memory pool start_free and a pointer to the end address end_free. The structure of the idle Block List node is as follows:

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

This structure can be seen as extracting 4 bytes from a memory block. When the memory block is idle, it stores the next idle block, when this memory block is delivered to the user, it stores the user data. Therefore, the idle block linked list in Allocator can be expressed:
OBJ * free_list [16];
3. Allocation Algorithm

// Algorithm: allocate // input: size of the applied memory // output: if the allocation is successful, a memory address is returned; otherwise, null {If (size greater than 128) is returned) start the first-level distributor to directly call malloc to allocate the required memory and return the memory address; else {increase the size to a multiple of 8 and obtain the corresponding header free_list_headif (free_list_head is not empty) from free_list based on the size) {remove the first idle block from the list and adjust free_list. Return free_list_head} else {call the refill algorithm to create the idle block list and return the required memory address }}// algorithm: refill // input: size of the memory block // output: create an idle block linked list and return the first available memory address {call the chunk_alloc algorithm to allocate several size-based continuous memory areas and return the chunk of the starting address and the number of successfully allocated blocks nobjif (number of blocks is 1) directly return chunk; else {starts to create free_list In the chunk address block. Based on the size, obtain the corresponding Header element free_list_head in free_list and point free_list_head to the address where the starting offset address of the chunk is size, that is, free_list_head = (OBJ *) (chunk + size) and then concatenate the remaining nobj-1 memory blocks in the entire chunk to form an idle list and return to chunk, that is, the first idle memory block in the chunk} // algorithm: chunk_alloc // input: size and size of the memory block, number of pre-allocated memory blocks nobj (passed by reference) // output: the address of a contiguous memory area and the number of memory blocks that can be accommodated in the area {calculate the total size of memory required total_bytesif (the memory pool is sufficient to allocate, that is, the end_free-start_free> = total_bytes) {If start_free is updated, the old start_free} else if is returned (nobj memory blocks are not allocated enough in the memory pool, but at least one can be allocated) {calculate the number of memory blocks that can be allocated, modify nobj, update start_free, and return the original start_free} else // no memory block can be allocated to a memory pool. {link the memory block of the memory pool call the malloc operation in the corresponding free_list to re-allocate the memory pool, total_bytes, which is twice the size, is the additional amount. start_free points to the returned memory address if (the allocation is unsuccessful) {If (there are still idle blocks in 16 idle lists) try to recycle 16 idle blocks from the idle list to the memory pool and then call chunk_alloc (size, nobj) else calls Level 1 distributor to check whether out of memory mechanism is still used} update end_free to start_free + total_bytes. total_bytes with heap_size twice calls chunk_alloc (size, nobj )}} // algorithm: deallocate // input: memory block address to be released P and size {If (size greater than 128 bytes) directly call free (P) release else {set the size to a multiple of 8 and obtain the corresponding idle list header pointer free_list_head accordingly. Adjust free_list_head and link P to the idle list block }}

In this scenario, free_list [2] already points to a 24-byte free block linked list. As shown in figure 1, when you apply for a 21-byte memory block from Allocator, allocaotr checks free_list [2] First, allocates the memory block referred to by free_list [2] to the user, and points the header to the next available idle block, as shown in figure 2. Note: When the memory block is on the linked list, the first four bytes are used to point to the next idle block. When allocated to the user, it is a common memory area.

Figure 1 Allocator status at a certain time


Figure 2 allocate a 24-byte memory block

4. Summary
The memory distributor in STL is actually based on the Free List allocation policy. The main feature is to optimize the allocation of small objects by organizing 16 free lists.
1) Fast allocation and release of small objects. After a fixed memory pool is pre-allocated at a time, the allocation and release operations for small memory blocks smaller than 128 bytes are only some basic pointer operations, compared to directly calling malloc/free, low overhead.
2) avoid Memory fragments. Random memory fragments will not only waste memory space, but also put pressure on the memory management of the OS.
3) Maximize the memory usage as much as possible. When the remaining idle areas in the memory pool are insufficient to allocate the required hours, the allocation algorithm will link them to the corresponding idle list, then, the system will try to find out whether there are suitable regions in the idle list,
However, this memory distributor is only used in STL containers and is not suitable for a general memory allocation. Because it requires that the size of the memory block must be provided when a memory block is released to determine which free list to recycle, and the STL container knows the size of the objects it needs to allocate, for example:
STL: vector <int> array;
Array indicates that the object size to be allocated is sizeof (INT ). A general memory distributor does not need to know the size of the memory to be released, similar to free (P ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.