Memory Pool implementation

Source: Internet
Author: User

Introduction

C/C ++ memory management is a headache for almost every programmer. allocate enough memory, track the memory allocation, and release the memory when it is not needed-this task is quite complicated. The direct use of the system to call malloc/free and new/delete for memory allocation and release has the following drawbacks:

  1. When calling malloc/new, the system needs to find an idle memory in the memory idle block table based on "first match", "Optimal Match", or other algorithms, and call free/delete, the system may need to merge idle memory blocks, which will produce additional overhead.
  2. When used frequently, a large amount of memory fragments are generated, thus reducing the program running efficiency.
  3. Easy to cause memory leakage


Memory Pool (memory pool) is a common method to directly call malloc/free and new/delete for memory management. When we apply for memory space, first, find the appropriate memory block in our memory pool, instead of directly applying to the operating system. The advantage is:

  1. Faster than malloc/free memory application/release
  2. Does not generate or rarely generate heap fragments
  3. Avoid Memory leakage


Memory Pool Design

Seeing that there are so many benefits to the memory pool, is it possible to abandon malloc/free immediately and rush to the memory pool? Slow. Before we implement the memory pool ourselves, we need to clarify the following issues:

  1. How to obtain the memory pool space? Is a large block of space allocated when the program is started, or is it allocated as needed when the program is running?
  2. Is there a size limit for the memory pool applied? If yes, what is the maximum memory block that can be applied?
  3. How can we reasonably design the memory block structure to facilitate memory application, tracing, and release?
  4. The more space the memory pool occupies, the less memory the other programs can use. Do you want to set the upper limit of the memory pool space? How many values are appropriate?

With the above problems, let's look at the following memory pool design scheme.


Memory Pool implementation solution 1

Download the source code of the memory pool.

First, the overall architecture of the solution is provided as follows:

Figure 1. Memory Pool Architecture

The structure mainly contains three structures: Block, list, and pool. The block structure contains pointers pointing to the actual memory space. forward and backward pointers allow the block to form a two-way linked list; in the list structure, the free Pointer Points to a linked list composed of idle memory blocks, and the used Pointer Points to a linked list composed of memory blocks used by the program. The size value is the size of the memory block, and the list is a one-way linked list; the pool structure records the head and tail of the list linked list.


Memory Tracking Policy

In this solution, 12 more bytes will be applied for during memory allocation, that is, the actual applied memory size is the size of the required memory + 12. The corresponding list pointer (4 bytes), used pointer (4 bytes), and verification code (4 bytes) are respectively stored in the 12 bytes that are added ). With this setting, we can easily get the list and block where the block memory is located, and the check code serves as a rough check for errors. The following figure shows the structure:

Figure 2. memory block Application

The arrows in the figure indicate the starting position of the memory block.


Memory application and release policies

Application: based on the size of the applied memory, traverse the list to check whether a matched size exists;

Matched size: NULL when viewing free

Free is null: Use malloc/New to apply for memory and place it at the end of the chain table indicated by used.

Free is not null: removes the header node of the Free linked list and places it at the end of the linked list indicated by used.

No matching size exists: Create a new list, apply for memory using malloc/new, and place it at the end of the chain table indicated by used of the List

Returns the memory space pointer.

Release: Get the list pointer and used pointer Based on the memory tracking policy, delete it from the linked list referred to by the used pointer, and place it in the linked list pointed to by the Free pointer.


Analysis of solution 1

Compared with the problems raised in the "Memory Pool Design" section, solution 1 has the following features:

  1. After the program starts, there is no memory block in the memory pool. It takes over the memory block management only when the program is applied for and released;
  2. This memory pool does not limit the size of the application to be submitted. It creates a linked list for each size value for memory management;
  3. This solution does not provide the function of limiting the memory pool size.


Combined with analysis, we can conclude that the application scenario of this solution is as follows: the memory block size applied by the program is relatively fixed (for example, only apply for/release 1024bytes or 2048bytes memory ), the application frequency and release frequency are basically the same (due to a large number of applications, releasing less will occupy too much memory and eventually lead to system crash ).


This article explains the basic knowledge of memory management and uses a simple memory pool implementation example as a stepping stone to help you understand the memory pool. The next article is an advanced article on memory pool, how to Implement the memory pool on the Apache server

The implementation of the Memory Pool (I) describes the reasons for using the memory pool, the issues to be considered in the design of the memory pool, and finally provides a simple example of implementing the memory pool. Using the memory pool implementation solution introduced in the previous article, we need to take a look at the more general memory pool implementation-Apache server memory pool implementation under certain conditions.


The Apache server developer sorts out the portable parts of the code and edits them into an Apache Portable Runtime Library (apacheportable run-timelibraries), or APR, which can be downloaded from here, this includes the implementation code of the memory pool to be introduced here. The Apache server memory pool is referred to as the APR memory pool.


APR Memory Pool Structure 1. Memory Allocation Node

Before learning about the entire memory pool architecture, let's first understand the most basic unit in the APR memory pool-the memory allocation node. The memory allocation node is used to describe the memory block allocated each time. The corresponding structure is apr_memnode_t, which is defined in the apr_allocator.h file. Its definition is as follows:

?
/* basic memory nodestructure*/struct
apr_memnode_t {  apr_memnode_t*next;          
/**< next memnode */    apr_memnode_t**ref;          
/**< reference to self */    apr_uint32_t  index;             
/**< size */    apr_uint32_t  free_index;      
/**< how much free */    char
*first_avail;          /**< pointer to first free memory */    char
*endp;                 /**< pointer to end of free memory */};

Even if each field in the structure is annotated in the source file, it is difficult to understand the purpose of each field. Here we will give a brief analysis of each field:

  1. Next: pointer to the next node;
  2. Ref: points to the next node of the previous node, and the next node of the previous node points to the current node. Therefore, ** ref is the current node;
  3. Index: indicates the size of the node and the index value of the linked list where the node is located;
  4. Free_index: The unoccupied space in the memory block described here (this is different from the above index and their literal meaning. Pay attention to it when you understand it );
  5. First_avail: pointer to the starting position of the available space;
  6. Endp: pointer to the end of the available space.

The node is as follows:

2. Memory distributor

In the ARP memory pool, a memory distributor is used to manage the memory allocation nodes. It is defined in apr_pools.c as follows:

?
struct
apr_allocator_t {    apr_uint32_t        max_index;    apr_uint32_t        max_free_index;    apr_uint32_t        current_free_index;    apr_pool_t          *owner;    apr_memnode_t  *free[MAX_INDEX];};
  1. Max_index: subscript of the Free pointer array. Free [max_index] points to the existing maximum memory block linked list;
  2. Max_free_index: maximum memory space that the memory distributor can accommodate;
  3. Current_free_index: The size of space that can be received by the memory distributor. It can be used with max_free_index to limit the size of the memory pool;
  4. Owner: indicates the memory pool to which the distributor belongs;
  5. Free: points to the head node of a linked list. each node in the linked list points to a linked list composed of Memory nodes. max_index is 20.


The following figure shows the Memory Distribution and Its managed Memory nodes:

We can clearly see that the subscript of the Free array from 1 to the MAX_INDEX-1, respectively pointing to a linked list with fixed node size, subscript increase 1, node size increase 4 K, therefore, the node size of the free [max_index] linked list is 84 K, which is the maximum "rule node" that the memory pool user can apply for. if the limit is exceeded, the subscripts 0 point to the linked list for management. To understand the relationship between free array subscript and node size, we need to know the macro definition apr_align:

?
#defineAPR_ALIGN(size, boundary) \                  
(((size)+ ((boundary) - 1)) &~((boundary) - 1))


This macro is nothing more than an integer that calculates the boundary nearest to the size. Generally, the size is an integer, while boundary must be a multiple of 2. For example, apr_align () is 8, apr_align () is 24, and apr_align () is 32.


For each space application, APR first alignment the space size:

?
size = APR_ALIGN(size +APR_MEMNODE_T_SIZE, 4096);

The result is that the size value is a multiple of 4096 (4 K, 2 to the power of 12). Finally, the Left shift corresponds to our Subscript:

?
index= (size >>BOUNDARY_INDEX) - 1;
//BOUNDARY_INDEX=12

3. Memory Pool Node

The APR memory pool node is defined as follows in the apr_pools.c file:

?
struct
apr_pool_t {    ……    apr_allocator_t      *allocator;    apr_memnode_t   *active;    ……};

There are many fields in this structure. We mainly focus on the two fields listed above.

  1. Allocator: points to the corresponding memory distributor;
  2. Active: pointer to the memory linked list in use

The memory pool node and its managed memory node are shown in:

It should be noted that although the structure is literally a "Memory Pool Structure", it is responsible for managing the memory in use.


APR memory pool Memory Management

After a preliminary understanding of the APR memory pool structure, we can see how APR uses these structures for memory management.


1. Memory Application

The Core Function Applied for memory is the allocator_alloc function. The parameter is a pointer to the memory distributor and the size of the space to be applied, memory allocation is to operate the memory distributor (for the fields listed below, see the "memory distributor" section). The memory application policy is as follows:

  1. The index is generated based on the size of the requested space. If the index value is 1 ~ Max_index is in the range of index ~ A piece of memory is returned from the linked list within the range of max_index;
  2. If the index value is> max_index, find a suitable memory in the free [0] linked list;
  3. If no idle memory block is found in the last two steps, a new memory block is returned through malloc (size.

2. Memory release

As mentioned in the memory application, if the memory distributor does not have a suitable memory block, it will call malloc to obtain one, but the newly allocated memory is not attached to the memory distributor linked list, instead, when allocator_free is called to release the memory, the memory can be mounted to the memory distributor linked list. The memory release policy is as follows:

  1. If the node size exceeds the full release threshold value of max_free_index, we cannot simply return it to the index linked list, but must completely return it to the operating system;
  2. If index <max_index, it means that the node is within the range of "rule node. Therefore, the node can be returned to the corresponding "rule linked list;
  3. If the node exceeds the range of the "rule node" but does not exceed the threshold value of max_free_index, we can place it in the header of the "index 0" linked list.

3. Memory managed by memory pool nodes

At the beginning, the linked list managed by the memory distributor was not attached to any memory. That is to say, the memory pool is empty. When we apply for memory, we must perform step 3 in "memory application, the newly allocated memory is managed by our memory pool node.

Let's take a look at a macro definition for memory pool node management:

?
/* Node list managementhelper macros; list_insert() inserts 'node'* before 'point'. */#define list_insert(node,point) do {         \    node->ref= point->ref;                     \    *node->ref= node;                          \    node->next= point;                         \    point->ref= &node->next;                \} while
(0)

When a memory pool node is created for the first time, the linked list status is as follows:

Apply for a node again and run list_insert (node, point). The result is as follows:

We can use allocator_free call or apr_pool_clear and apr_pool_destroy call (they call allocator_free internally) to release memory pool nodes, the released memory will be returned to the memory pool or operating system according to the "memory release" policy.


Summary

The double meaning of the sub-mark of the Free array (that is, the array subscript and memory block size), and the limit on the memory pool size through the que value max_free_index are hard to understand, but it is also part of the APR memory pool implementation. In addition, the APR memory pool also involves the concept of parent/child/sibling memory pool and memory pool lifecycle, students can learn more deeply through the reference documents provided at the end of the article.


Reference: Apache memory pool insider

 

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.