Description: I think to read the source code of allocator part of STL, and draw its thought, at least the following knowledge you want to understand: operator new and operator delete, handler function and a bit of template knowledge. Otherwise, you may not be able to see the following, add some knowledge to learn STL source is better.
The following is a combination of the key source code Analysis C++stl (SGI version) of the memory Configurator design ideas. The key word is " thought ", so the focus is also on the horizon.
1. Brief introduction of Allocator
I read the source code is SGI's version, but also the most clear version, the name of the most easy to read. Allocator someone calls it a space Configurator , because space is not necessarily memory, or it can be a disk or other secondary storage medium. The memory configuration I'm talking about refers to the allocator.
The C + + standard regulates some necessary interfaces of allocator, which are implemented by various manufacturers. The SGI version is different from the standard specification, and its name is alloc rather than allocatorand does not accept any parameters .
Let's say you write a allocator in the program and you can't write it like this:
vector<int, std::allocator<int> > IV; // of the wrong
It has to be written like this:
vector<int, std::alloc> IV // Good
Although SGI STL does not conform to specifications, we use it as if it were natural. This is because the Space Configurator is the default when we use it, and we do not need to specify it ourselves. For example, the declaration of vectors in the STL is as follows:
Note: I'm basically explaining the code below, because I find it clearer (color contrast) than pasting the code.
2. Simple introduction of source files
STL Standard stipulation: STL's allocator definition in <Memory> file,<memory> mainly contains some header files, we mainly say is two:
<stl_alloc.h> responsible for memory space configuration and release;<stl_construct.h> the construction and destruction of the object content
3. Construction and destruction tools: Construct () and destroy ()
Let's start with a simple <stl_construct.h> file. This part also does not involve what thought, just have a version of Destroy () should take a serious look.
(1) Construction Tool: Construct ()
Construct () has only one version:
This uses the placement new expression (locating the new expression), which is the function of P pointing to a memory type of T1, which initializes the memory with value values.
(2) destructor tools
There are several versions of Destroy ():
First version:
This shows the practice of calling destructors, which you should also be familiar with.
A second version:
The second version can be a bit of a statement. The call hierarchy is such that:destroy-> __destroy-> __destroy_aux,__destroy_aux eventually calls the first version of Destroy. This version of destroy accepts a pair of iterators as parameters that the destructor points to within the range of elements.
Before explaining this process, let's briefly say trivial_destructor.
If the user does not define a destructor, but is synthesized with a compiler, then the destructor is basically useless (but is called by default), called trivial destructor.
Then, if a pair of iterators point to elements that are trivial destructor, there is no need to waste time executing its destructor on each object sequentially, depending on the compiler's behavior. This is an improvement in efficiency. This is a point of STL allocator optimization.
First, the Value_type () is used to obtain the type of the iterator to the object, and then the __type_traits<t> is used to determine whether the object's destructor is Trivial_destructor. If it is __true_type do nothing, otherwise the loop calls the first version of Destroy ().
A third version:
This is a special version of iterators for char* and wchar_t*, and seeing that their function body is empty, you should have guessed that there is no need to perform a destructor operation.
4. Application and destruction of memory, Std::alloc
Memory of the application and destruction by <stl_alloc.h> responsible. SGI's philosophy of design on this point is:
(1) Require space for the system heap.
(2) Consider multithreading status.
(3) Consider the contingency strategy when the memory is low.
(4) Consider the possible memory fragmentation problems caused by too many "small chunks".
In fact, the main thing I want to say is (3) (4) Design strategy, especially memory pool ideas.
Std::alloc's overall design philosophy is:
SGI designed a two-tier Configurator, the first-level configurator directly uses malloc and free, and the second-level Configurator uses different strategies depending on the situation: when the configuration chunk exceeds 128bytes, it is considered "large enough" to call the first-level configurator processing, and when the configuration chunk is less than 128bytes, it is considered " Too small ", in order to reduce the additional burden, using memory pool processing mode, no longer rely on the first level configurator.
First level Memory Configurator resolution
The primary functions of the first level Configurator are: Allocate allocating memory, deallocate freeing memory, reallocate memory redistribution, etc.
(1) Allocate
Call the C function malloc directly and call the Oom_malloc function if the memory does not meet the requirements.
Originally, this is oneself realizes the handler function, ah, why do you realize it? The C++new-handler mechanism cannot be used because it is not using memory operator new configuration.
There are actually a lot of things to say about this mechanism, and if you're not familiar with its purpose or how it's done, I suggest you look at effective C + +, or take a look at my notes on effective C + +. I'm not here primarily to analyze grammatical aspects of things.
(2) deallocate
It should be clear that the code is put on.
(3) Reallocate
This is still the REALLOC function that calls C, and if the call fails, the Oom_realloc function is called.
It can be seen that Oom_realloc is also a handler function.
Basically the first level memory Configurator is explained. One more point:SGI configures memory with malloc instead of operator new for historical reasons, while C + + does not provide realloc functions . This resulted in SGI not being able to use C + + 's Set_new_handler () directly, only to emulate one on its own. How to simulate Set_new_handler, there is a specific pattern.
Second, secondary memory configurator resolution
The second-level memory Configurator adds mechanisms to avoid memory fragmentation caused by too many small chunks. Small chunks bring not only memory fragmentation, but also a big problem with the additional burden of configuration . The burden can never be avoided, after all, the system relies on this extra space to manage memory, but the smaller the chunk, the greater the proportion of the burden, the more natural the more wasted.
The overall idea of the second-level memory Configurator is:
(1) if the requested chunk exceeds 128bytes, it will be processed by the first level memory configurator.
(2) if the requested chunk is less than or equal to 128bytes, it is managed with a memory pool.
The second-level memory configurator will increase the memory requirements of any small chunk to a multiple of 8 and maintain 16 free-lists, each with a management size of 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes of small chunks.
The node structure in Free-lists is as follows: (I have already annotated this Union)
Note:This use of Union is also known as a "flexible array" member. In essence, this is a technique related to the small-end alignment of this storage method.
(1) Allocate
The memory allocation interface of the second-level memory Configurator __default_alloc_template is the allocate function.
I've commented on the key section with a red frame. The Freelist_index (n) function returns the subscript of the appropriate list in 16 free-list based on the value of N.
And look at Round_up (n), this function I think is very clever, the bytes value is adjusted to a multiple of 8 bytes.
To understand this function, you can give a few bytes values to see what the return value is and naturally understand it. Refill () function is very useful, I put it below to introduce.
(2) deallocate ()
First to determine the size of the chunk, greater than 128 bytes to call the first level configurator, otherwise, according to the size of the bytes to be recycled, determine which should be returned to the free list, and then by the free list is recycled.
(3) Refill ()
This function is very important, so you have to come up with a separate introduction. When there are no chunks available in the free list, this function is called to repopulate a portion of the space for it. The new space is taken from the memory pool (completed by Chunk_alloc). By default, 20 new chunks are obtained, and if there is insufficient memory pool space, the number of new chunks obtained may be less than 20.
The two red boxes in the figure are two noteworthy points. Once the memory chunk has been obtained from the memory pool, take out one to the caller, and find the appropriate free list to "wear" it.
(4) Memory pool function Chunk_alloc ()
The memory pool has been used to supply the free list. This function is explained separately below.
First Branch: see if the capacity in the memory pool is enough, just take it right away.
The Second branch: the memory pool is not enough 20 blocks of capacity, but greater than or equal to the length of 1 blocks, the rest is given out. At this point, the memory pool is empty .
The Third branch: If the memory pool does not have 1 corresponding blocks to provide, such as 32 bytes, but only 8 bytes, it is best to link the 8 bytes to the corresponding free list utilization. Not surprisingly, the memory pool is empty at this time.
Fourth Branch: The memory pool is empty, so the memory pool turns to the runtime heap, and the heap doesn't have that much space, so check out which blocks in the 16 free list are useless, and add these to the memory pool.
Fifth Branch: Yes, the heap is powerless. The memory pool simply calls the first-level configurator, because the first-level configurator has a new-handler mechanism, and perhaps has the opportunity to release other memory to be called here. If possible, the Bad-alloc exception is thrown if it succeeds.
Summary:
If someone asks me the idea of STL memory configuration. I might say: C++stl is a level two configuration memory, specifically: the first level is responsible for the management of large chunks of memory, to ensure that there is a similar new-handler mechanism; the second level is responsible for the management of small chunks of memory, in order to better manage memory fragmentation, build 16 linked lists, each linked list "wear" A piece of fixed-size memory, the 16 linked list (0 to 15) respectively "Wear" the memory is 8, 16, 24 ... 128 times times the number of relationships. Need memory, from the "appropriate" list to take away, if the "appropriate" list of memory is not enough, from the memory pool, if the memory pool is not enough, from the runtime heap, if the heap also overflow, the first level configurator, because it has a new-handler mechanism.
So, the stuff on the heap ran out and swapped it back, don't let the memory pool worry .
Design idea and key source analysis of C++STL memory configuration