To further understand the operating principles of Allocator, we use the following as an example to explain its implementation details.
STD: Allocator <int> myall;
Myall. Allocate (10 );
The above code calls sgi stl Allocator and allocates 10 int-type space. The following analysis uses the sgistl source code allocator. cpp and adds my understanding comments. It is recommended that you refer to Hou Jie's book for more information. However, the actual version I currently use is slightly different from that used by Hou Jie, including the _ S _ sign before the function name, the new version of memory block management uses the stack management method (although there is actually only one element at most, it may be extended in the future), but it has little impact on understanding the principle.
The STD: allocator class implemented by the STL interface calculates the sizeof (INT) * 4 = 40 of the space to be allocated, and then calls the following function allocated by the node to implement it.
Determine whether to use a level-1 or level-2 distributor based on the provided parameter _ n (that is, the requested memory space. In STL implementation, both Microsoft and SGI implement _ max_bytes to get 128.
static void* _STLP_CALL allocate(size_t& __n) { return (__n > (size_t)_MAX_BYTES) ? __stl_new(__n) : _M_allocate(__n); }
If the number is greater than 128, newoperator is used directly to apply for memory. There is nothing worth mentioning. If the value is smaller than or equal to 128, the second-level sub-distributor can reduce memory fragments and improve the memory utilization. We will discuss the situations where the value is less than or equal to 128.
STL maintains a 16 member _ s_free_list array and manages the space linked lists from 8 to 128 bytes respectively. When you need to apply for 1-bytes of space, it searches for the corresponding linked list, if a node exists, it is directly allocated and returned to the program. If the linked list is insufficient, _ s_refill is used to apply for a large memory space.
Void * _ node_alloc_impl: _ m_allocate (size_t & _ n) {_ n = _ s_round_up (_ n ); // increase _ n to a multiple of 8 to reduce memory fragments _ OBJ * _ r = _ s_free_list [_ s_freelist_index (_ n)]. pop (); // query whether the corresponding linked list has available nodes if (_ r = 0) {_ r = _ s_refill (_ n );} // if no node is available, apply for and return from the large memory space (if more space is applied for in this linked list for backup ). Return _ r;}/* returns an object of size _ n, and optionally adds additional ones to * // * freelist of objects of size _ n. * // * we assume that _ n is properly aligned. */_ node_alloc_impl: _ OBJ * _ node_alloc_impl: _ s_refill (size_t _ n) {// at this time, _ n is 40 int _ nobjs = 20; // char * _ chunk = _ s_chunk_alloc (_ n, _ nobjs), which is assigned a space of 20 times the required node size by default. // apply to the large memory: eldest brother, I need 20 _ n spaces to do things. I 'd better divide them into 20. If not, how much can I do. If (_ nobjs <= 1) return _ reinterpret_cast (_ OBJ *, _ chunk); // do not mention 20. Currently, none of them can be allocated! // Push all new nodes (minus first one) onto freelist _ OBJ * _ result = _ reinterpret_cast (_ OBJ *, _ chunk ); // record the allocated first Node space as a return item. _ OBJ * _ cur_item = _ result; _ freelist * _ my_freelist = _ s_free_list + _ s_freelist_index (_ n ); // put all the extra space in the corresponding linked list for use. This is saved on the chain with a size of 40 bytes. For (-- _ nobjs; _ nobjs! = 0; -- _ nobjs) {_ cur_item = _ reinterpret_cast (_ OBJ *, _ reinterpret_cast (char *, _ cur_item) + _ n ); _ my_freelist-> push (_ cur_item);} return _ result ;} /* we allocate memory in large chunks in order to avoid fragmenting * // * the malloc heap too much. * // * we assume that size is properly aligned. * /// request 20 40-bytes space for large memory size! Here _ p_size is 40 ,__ nobjs is 20char * _ node_alloc_impl: _ s_chunk_alloc (size_t _ p_size, Int & _ nobjs) {char * _ result = 0; _ add_atomic_t _ total_bytes = _ static_cast (_ add_atomic_t, _ p_size) * _ nobjs; // The memory block also maintains a memory pool linked list _ s_free_mem_blocks, in fact, it only maintains one piece, sometimes not even one piece. _ Freeblockheader * _ block = _ static_cast (_ freeblockheader *, _ s_free_mem_blocks.pop (); If (_ block! = 0) {// We checked a block out and can now mess with it with impugnity. // We'll put the remainder back into the list if we're done with it below. char * _ buf_start = _ reinterpret_cast (char *, _ block); // check whether the memory block contains _ bytes_left space. _ Add_atomic_t _ bytes_left = _ block-> _ m_end-_ buf_start; // compare whether it meets our 40*20 requirements. If (_ bytes_left <_ total_bytes) & (_ bytes_left> = _ static_cast (_ add_atomic_t, _ p_size) {// ha, I am so many, it is enough to give you a 40, but it cannot reach 20 40 bytes! _ Result = _ buf_start; // note that _ nobjs = (INT) (_ bytes_left/_ p_size) has been allocated to the space ); _ total_bytes = _ static_cast (_ add_atomic_t, _ p_size) * _ nobjs; _ bytes_left-= _ total_bytes; _ buf_start + = _ total_bytes ;} else if (_ bytes_left> = _ total_bytes) {// The block has enough left to satisfy all that was asked for // There are many things in the cell, it's enough space for 20 40 bytes. You can use it! _ Result = _ buf_start; // note that _ bytes_left-= _ total_bytes; _ buf_start + = _ total_bytes ;} if (_ bytes_left! = 0) {// The memory block is still available (no matter whether a 40 is connected or 20 40 bytes is finished, there are more) // there is still some memory left over in block after we satisfied our request. /* space has been allocated, but there are still more (because of the need to maintain the memory block linked list, the Left Block must be larger than the structure maintained by the memory block linked list, the specific mechanism is a small memory saving technique, which is described in Mr. Hou's book). It can be put back into the memory block linked list. */If (_ result! = 0) & (_ bytes_left> = (_ add_atomic_t) sizeof (_ freeblockheader ))) {// we were able to allocate at least one object and there is still enough // left to put remainder back into list. _ freeblockheader * _ newblock = _ reinterpret_cast (_ freeblockheader *, _ buf_start); _ newblock-> _ m_end = _ block-> _ m_end; _ s_free_mem_blocks.push (_ newblock);} else {// we were not able to allocate enough for at lea St one object. // shove into freelist of nearest (rounded-Down !) Size. // No space is allocated, or the space left is smaller than the size of the structure maintained by the memory block linked list (on 32-bit machines, the size required for this maintenance is 8 bytes in total for two pointers, 64-bit machine is 16 bytes), and the small memory is allocated to the free linked list of the corresponding size. Size_t _ rounded_down = _ s_round_up (_ bytes_left + 1)-(size_t) _ align; If (_ rounded_down> 0) _ s_free_list [_ s_freelist_index (_ rounded_down)]. push (_ OBJ *) _ buf_start) ;}} if (_ result! = 0) return _ result; // if it has been allocated, return it!} // We couldn't satisfy it from the list of free blocks, get new memory. /* if no allocation has been made, start the assignment now, and the Lions open their mouths more than twice. A little more is required to avoid the need for memory in the system. Here _ s_heap_size is an incremental trace of the adjustment, which is the value added to _ bytes_to_get> 4 during the last allocation (the calculation can be seen below ), the more allocation times, the larger the value will be automatically adjusted. This allows you to allocate more resources at a time and minimize the allocation times. At the beginning, the value of _ s_heap_size is 0. */_ Add_atomic_t _ bytes_to_get = 2 * _ total_bytes + _ static_cast (_ add_atomic_t, _ s_round_up (_ static_cast (_ uadd_atomic_t, _ stlp_atomic_add (& _ s_heap_size, 0) + _ stlp_offset; _ stlp_try {_ result = _ stlp_new_chunk (_ bytes_to_get ); // operator new will be called directly here to allocate drops!} // The Memory Allocation Error capture function segment that does not affect understanding the concept is omitted here. _ Stlp_atomic_add (& _ s_heap_size, _ bytes_to_get> 4); If (_ bytes_to_get> _ total_bytes) {// push excess memory allocated in this chunk into list of free memory blocks // The remaining memory is managed in the memory block linked list for future use. _ Freeblockheader * _ freeblock = _ reinterpret_cast (_ freeblockheader *, _ result + _ total_bytes); _ freeblock-> _ m_end = _ result + _ bytes_to_get; _ s_free_mem_blocks.push (_ freeblock); // as mentioned above, it maintains a maximum of one memory block.} Return _ result ;}