Home > Article > Backend Development > Detailed explanation of php memory management
In C, malloc and free are used directly to allocate and release memory. However, frequent allocation and release of memory will cause memory fragmentation and reduce system performance. PHP variables will be allocated and released very frequently. If you directly use malloc Allocating in this way will cause serious performance problems. As a language-level application, this loss is not acceptable, so PHP implemented its own memory pool ZendMM to replace glibc's malloc and free to solve the problem of frequent memory allocation. , the issue of release.
It defines memory operations at three granularities: chunk, page, and slot. The size of each chunk is 2MB, and each page is 4kB. One chunk is cut into 512 pages, and each page is cut into several slots to apply for memory. Different allocation strategies are implemented according to the size of the application.
huge: larger than 2MB, directly call the system allocation, allocate several chunks
large: apply for memory larger than 3092B (3/4page), smaller than 2044kb (511page) allocation Several pages
small: The application memory is less than 3092B. The memory pool has defined 30 different sizes of memory (8, 16, 32..., 3072) in advance. They are distributed on different pages. When applying for memory, you can directly Find the corresponding slot on the corresponding memory. The memory pool stores the main information of the memory pool through the zend_mm_heap structure, such as large memory linked list, chunk linked list, memory linked list of each slot size, etc.
Large memory is actually allocated to several chunks. Then it is managed through a zend_mm_huge_list structure. A one-way linked list is formed between large memories.
Chunk is the minimum granularity for the memory pool to apply for and release memory from the system. A doubly linked list is formed between chunks. The address of the first chunk is stored in zend_mm_heap- >main_chunk, the memory of the first page is used to save the structure members of the chunk itself, such as the pointers of the previous and next chunks, the usage of each page of the current chunk, etc.
Slots of the same size form a singly linked list
The initialization of the memory pool is completed in the php_module_startup stage. The initialization process is mainly to allocate the heap structure. This process is completed in the start_memory_manager process. If it is a multi-threaded environment, a memory pool will be allocated for each thread, and the threads will not affect each other. During initialization, the environment variable use_zend_alloc_huge_pages will be used to set whether to enable memory huge pages. In a non-thread-safe environment, the allocated heap will be saved in alloc_globals, which is the AG macro. It should be noted that the zend_mm_heap structure is not allocated separately and is embedded in In the chunk structure, because the chunk structure occupies a page, but in fact it does not use that much memory, in order to utilize the space as much as possible, it is placed here
Memory allocation: 1. Huge memory allocation exceeding 2MB will be aligned to n chunks when allocated, and a zned_mm_huge_list structure will also be allocated to manage all huge memory. The memory alignment process is adjusted by the memory pool itself after application, not simply It is completed by the operating system. It will apply once according to the actual size. If the memory alignment can just be achieved, no adjustment is needed and it will be returned to use directly. If it is not an integer multiple of ZEND_MM_CHUNK_SIZE (2MB), zendMM will release the memory and then follow the instructions. Actual memory size + ZEND_MM_CHUNK_SIZE Apply again. The extra block is used for adjustment. 2. Large allocation: When the applied memory size is between 3072B (3/4page) and 2044k (511 pages), the memory pool will be in Search the corresponding number of pages on the chunk and return it. The application granularity of large is page. There are two members, free_map and map, on the chunk to record the allocation information of the page.
free_map is 512bit, which is used to record the allocation of pages on the chunk. Set to 1 if used Start traversing from the first chunk and check whether the chunks have pages that meet the requirements. If the current chunk does not have a suitable page, then search for the next chunk. If there is no suitable one until the end, then re-allocate a chunk. When applying, I don’t know Whoever finds a sufficient number of pages should fill the chunk gaps as much as possible and connect them with the allocated pages as much as possible to avoid page gaps in the middle (to reduce the number of searches during subsequent allocation) 1) From The first chunk group (0~63) starts to check. There are available pages in the current group. First check the bits of the current page and find the positions of the first and last free pages. If there are not enough, mark these pages as 1 ( Allocated), search for other groups. If the page is just right, use it directly and interrupt the retrieval. If the page is larger than required, it means it is available, but it is not optimal. It will continue to search for other chunks until an optimal one is finally found ( can maximize the use of the page) 2) After finding the appropriate page, set the corresponding page information, that is, free_map and map information, and then return the page address 3. Small allocation:
will first check whether the memory of the corresponding specifications has been allocated. If it is not allocated or the allocation has been used up, apply for the corresponding number of pages. The page allocation process is the same as that of large allocation. After applying for the page, it is cut into slots according to the fixed size. The slots are connected with a single linked list, and the head of the linked list is saved. To AG(mm_heap)->free_slot
The granularity of memory pool release is chunk, which is completed through efree. 1. The release of huge, large, and small memory types is because the first page of the chunk is occupied, so it is impossible to have an offset of 0 relative to the chunk, so it can be distinguished When releasing the chunk type and the large and small types, the occupied chunk will be released and deleted from the AG linked list at the same time. 2. Release of large memory: If the calculated offset is not 0, it means that the address is large or small memory, and then according to The offset calculates the page number. After getting the page, you can get the allocation type of the page from the chunk->map, and you can release the memory of the specified type. Large will not release it directly, but will set the allocation information of the page to Unallocated. If it is found that the chunk is unallocated after release, the chunk will be released. When releasing, it is preferred to move the chunk to AG. After the cache number reaches a certain value, the newly added chunk will no longer be cached and the memory will be returned. System, avoid occupying too much memory. When allocating chunks, if a cached chunk is found in chached_chunks, it can be directly taken out and used without making an application to the system. 3. Small type release, directly insert the released slot back into the available linked list of the rule slot. Just the head, relatively simple
In C, malloc and free are used directly to allocate and release memory. However, frequent allocation and release of memory will cause memory fragmentation and reduce system performance. PHP variables will be allocated and released very frequently. If you directly use malloc Allocating in this way will cause serious performance problems. As a language-level application, this loss is not acceptable, so PHP implemented its own memory pool ZendMM to replace glibc's malloc and free to solve the problem of frequent memory allocation. , Release issues
It defines memory operations at three granularities: chunk, page, and slot. The size of each chunk is 2MB, and each page is 4kB. A chunk is cut into 512 pages, and each page is cut. into several slots. When applying for memory, different allocation strategies are implemented according to the size of the application.
huge: greater than 2MB, directly call the system allocation, allocate several chunks
large: apply for memory greater than 3092B (3/4page) , less than 2044kb (511page) allocate several pages
small: apply for memory less than 3092B, the memory pool has defined 30 different sizes of memory in advance (8, 16, 32..., 3072), they are distributed in different On the page, when applying for memory, directly search the corresponding slot in the corresponding memory. The memory pool stores the main information of the memory pool through the zend_mm_heap structure, such as large memory linked list, chunk linked list, memory linked list of each slot size, etc.
Large memory allocation It is actually several chunks, which are then managed through a zend_mm_huge_list structure. A one-way linked list is formed between large memories.
Chunk is the minimum granularity for the memory pool to apply for and release memory from the system. A two-way linked list is formed between chunks. The first The address of the chunk is stored in zend_mm_heap->main_chunk. The memory of the first page is used to save the structure members of the chunk itself, such as the pointers of the previous and next chunks, the usage of each page of the current chunk, etc.
Between slots of the same size Forming a single linked list
The initialization of the memory pool is completed in the php_module_startup stage. The initialization process is mainly to allocate the heap structure. This process is completed in the start_memory_manager process. If it is a multi-threaded environment, a memory pool will be allocated for each thread. Threads do not affect each other. During initialization, the environment variable use_zend_alloc_huge_pages will be used to set whether to enable memory huge pages. In a non-thread-safe environment, the allocated heap will be saved in alloc_globals, which is the AG macro. It should be noted that the zend_mm_heap structure It is not allocated separately, but is embedded in the chunk structure, because the chunk structure occupies a page, but in fact it does not use that much memory. In order to utilize the space as much as possible, it is placed here
Memory allocation: 1. Huge memory allocation exceeding 2MB will be aligned to n chunks when allocated, and a zned_mm_huge_list structure will also be allocated to manage all huge memory. The memory alignment process is the memory pool itself after application The adjustment is not simply done by the operating system. It will be applied once according to the actual size. If the memory alignment can just be achieved, no adjustment is needed and it will be returned to use directly. If it is not an integer multiple of ZEND_MM_CHUNK_SIZE (2MB), zendMM will release it. For this piece of memory, apply again according to the actual memory size + ZEND_MM_CHUNK_SIZE. The extra piece of memory applied for is used for adjustment. 2. Large allocation: The applied memory size is between 3072B (3/4page) and 2044k (511 pages). time, the memory pool will search for the corresponding number of pages on the chunk and return them. The application granularity of large is page. There are two members free_map and map on the chunk to record the allocation information of the page.
free_map is 512bit, which is used to record the page. The allocation status of the page on the chunk. If it is used, it is set to 1
. The map is used to record the allocation type of the page and the number of allocated pages. Each page corresponds to an array member. The highest 2 bits record the allocation type of the page. 01 is Large and 10 are small. When allocating, start traversing from the first chunk and check whether the chunk has a page that meets the requirements. If the current chunk does not have a suitable page, search for the next chunk. If there is no suitable page until the end, reallocate it. When applying for a chunk, I don’t know who has found enough pages, but fill the gaps in the chunk as much as possible, and connect them with the allocated pages as much as possible to avoid page gaps in the middle (to reduce the time of subsequent allocation) The number of searches) 1) Start checking from the first chunk group (0~63). If the current group has available pages, first detect the bits of the current page and find the positions of the first and last free pages. If there are not enough, then These pages are marked as 1 (allocated), and other groups are searched. If the page is just right, it is used directly and the retrieval is interrupted. If the page is larger than required, it means it is available, but it is not optimal, and other chunks will be searched until the end. Compare the optimal one (the one that can maximize the use of the page) 2) After finding the appropriate page, set the corresponding page information, that is, free_map and map information, and then return the page address 3. Small allocation:
will check the corresponding first Whether the specified memory has been allocated? If it has not been allocated or the allocation has been used up, apply for pages with the corresponding number of pages. The page allocation process is the same as that of large allocation. After applying for the page, it is cut into slots according to the fixed size, and the slots are divided into single slots. Linked list connection, the head of the linked list is saved to AG(mm_heap)->free_slot
The granularity of memory pool release is chunk, which is completed through efree. 1. The release of huge, large, and small memory types is because the first page of the chunk is occupied, so it is impossible to have an offset of 0 relative to the chunk, so it can be distinguished When releasing the chunk type and the large and small types, the occupied chunk will be released and deleted from the AG linked list at the same time. 2. Release of large memory: If the calculated offset is not 0, it means that the address is large or small memory, and then according to The offset calculates the page number. After getting the page, you can get the allocation type of the page from the chunk->map, and you can release the memory of the specified type. Large will not release it directly, but will set the allocation information of the page to Unallocated. If it is found that the chunk is unallocated after release, the chunk will be released. When releasing, it is preferred to move the chunk to AG. After the cache number reaches a certain value, the newly added chunk will no longer be cached and the memory will be returned. System, avoid occupying too much memory. When allocating chunks, if a cached chunk is found in chached_chunks, it can be directly taken out and used without making an application to the system. 3. Small type release, directly insert the released slot back into the available linked list of the rule slot. Just the head, relatively simple.
Related recommendations:
Explanation of JS memory management examples
Detailed introduction to memory management in Linux
Study notes on PHP variables and memory management
The above is the detailed content of Detailed explanation of php memory management. For more information, please follow other related articles on the PHP Chinese website!