Multi-thread memory management method and system fusing large pages and thread-local allocation zones
By employing a memory management approach that combines large pages and thread-local allocation areas in a multi-threaded environment, the problems of thread contention and memory fragmentation in traditional memory management are solved, achieving efficient memory access and utilization and improving the performance and stability of multi-threaded systems.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- BEIJING JINAN CHUANGSHI TECHNOLOGY CO LTD
- Filing Date
- 2026-01-06
- Publication Date
- 2026-06-16
Smart Images

Figure CN121705032B_ABST
Abstract
Description
Technical Field
[0001] This invention relates to the field of memory management technology, and in particular to a multi-threaded memory management method and system that integrates big pages and thread-local allocation areas. Background Technology
[0002] With the rapid development of high-performance computing and big data processing applications, multithreaded programs are increasingly demanding higher memory management efficiency. Traditional memory management mechanisms face performance bottlenecks, especially in multi-core processor systems. Current mainstream memory management methods mainly include the system default memory allocator, third-party memory allocation libraries, and custom memory pools. The system default memory allocator typically uses a general design and cannot be optimized for specific application scenarios; while third-party memory allocation libraries provide more efficient memory management mechanisms, they often lack effective support for large page memory; and custom memory pools require developers to invest significant effort in design and maintenance.
[0003] In multithreaded environments, traditional memory allocation methods typically rely on global locks to ensure memory allocation consistency, leading to severe thread contention issues. As the number of processor cores increases, this contention becomes an increasingly significant bottleneck for system performance. Memory management models with standard page sizes increase page table overhead and TLB miss rates, reducing memory access efficiency. Existing memory allocators often lack effective mechanisms for managing memory fragmentation, resulting in decreased system memory utilization and even memory allocation failures after prolonged operation.
[0004] Existing technologies still have some defects and shortcomings. Traditional memory allocators use global locks to protect shared resources in multi-threaded environments, leading to severe competition between threads in high-concurrency scenarios, which becomes a performance bottleneck. Conventional memory allocation mechanisms are based on standard page size design and fail to make full use of large page memory technology to reduce TLB missing and page table overhead, affecting the efficiency of high-performance computing and big data processing applications. Existing memory allocation strategies lack efficient memory reclamation and reuse mechanisms, and cannot effectively deal with memory fragmentation problems generated during long-term operation, reducing the system's memory utilization and stability. Summary of the Invention
[0005] The present invention provides a method and system that can solve the problems in the prior art.
[0006] A first aspect of this invention provides a multi-threaded memory management method that integrates massive pages and thread-local allocation areas, comprising:
[0007] Request a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool;
[0008] The global memory pool is divided into multiple thread-local allocation areas. A consistent hashing algorithm is used to map worker threads to the thread-local allocation areas, and the starting address of the thread-local allocation area is stored in the thread-local storage variable.
[0009] Within the thread-local allocation area, a multi-level size-classified free list is established, in which the free list with a level value greater than the target level is determined as the parent free list;
[0010] When a worker thread requests memory allocation, it reads the thread-local allocation area from the thread-local storage variable, maps the requested size to the target level, and if the free list at the target level is empty, it obtains memory blocks from the parent free list and splits them to the target level. If the parent free list is empty, it batch-splits memory blocks from the unallocated space.
[0011] When a worker thread releases a memory block, it uses bitwise operations to retrieve a buddy block and calculates the merge parameters. If a buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively.
[0012] The free memory percentage is calculated based on the bitmap. If it exceeds the preset free threshold, the level to be reclaimed is selected based on the heat score. The free memory blocks of the level to be reclaimed are aggregated and returned to the global memory pool. This includes maintaining a free memory bitmap in the metadata area of the thread local allocation area. Each bit in the bitmap corresponds to the occupancy status of a smallest granular memory block. Each time memory is allocated or released, the corresponding position in the bitmap is set or cleared.
[0013] By performing segmented parallel bit counting operations on the bitmap, the number of free bits is counted, and the percentage of free memory is calculated based on the number of free bits and the total number of bits in the bitmap.
[0014] Maintain an access timestamp queue for each level of the free list. Each time access occurs, the current timestamp is enqueued. Calculate the access density of each level within a preset time window. Weight the access density with the memory block size to obtain a popularity score.
[0015] Determine if the percentage of free memory exceeds the preset free threshold. If it does, select the level with the lowest popularity score as the level to be reclaimed.
[0016] Memory blocks in the free list to be reclaimed are aggregated according to address contiguity. Multiple memory blocks with contiguity are merged into an aggregated memory block, and the target level of the aggregated memory block in the global memory pool is calculated.
[0017] The aggregated memory blocks are pushed in batches to the recycling buffer of the global memory pool. Based on the background thread of the global memory pool, the insertion operation is completed asynchronously, and the bit state of the corresponding area in the bitmap is updated.
[0018] In one optional embodiment, the global memory pool is divided into multiple thread-local allocation areas, and a consistent hashing algorithm is used to map worker threads to the thread-local allocation areas. Storing the starting address of the thread-local allocation area in a thread-local storage variable includes:
[0019] The size of a single thread-local allocation area is calculated based on the preset number of partitions. The address space of the global memory pool is divided into multiple thread-local allocation areas according to the calculated size. A unique partition identifier is assigned to each thread-local allocation area. The partition identifier is hashed to obtain the partition hash value. The partition hash value is mapped to different positions of the hash ring in numerical order to form a consistent hash ring. A mapping table between the partition identifier and the starting address of the thread-local allocation area is established for the consistent hash ring.
[0020] When a worker thread is initialized, its thread identifier is obtained, and a hash operation is performed on the thread identifier to obtain the thread hash value. The first partition hash value that is greater than or equal to the thread hash value is searched in a clockwise direction on the consistent hash ring. The corresponding partition identifier is determined based on the found partition hash value. The starting address of the thread local allocation area corresponding to the partition identifier is looked up through the mapping table, and the starting address is written into the worker thread's thread local storage variable to establish the binding relationship between the worker thread and the thread local allocation area.
[0021] In one optional embodiment, when a worker thread requests memory allocation, the thread-local allocation area is read from the thread-local storage variable, the requested size is mapped to the target level, and if the free list at the target level is empty, a memory block is obtained from the parent free list and split to the target level. If all parent free lists are empty, then the batch splitting of memory blocks from unallocated space includes:
[0022] When a worker thread initiates a memory allocation request, it reads the thread-local allocation area from the thread-local storage variable, obtains the requested size, performs a bitwise AND operation with the preset bitmask, performs a shift operation on the result to obtain the target level, and accesses the free list corresponding to the target level.
[0023] If the target level free list is empty, traverse the parent free list, find the first non-empty parent free list, extract a memory block from the first non-empty parent free list as the memory block to be split, calculate the difference between the size of the memory block to be split and the size corresponding to the target level, determine the number of splits based on the difference, perform the split operation corresponding to the number of splits on the memory block to be split, return the target size memory block obtained after splitting to the worker thread, and insert the intermediate size memory block generated during the splitting process into the free list of the corresponding level.
[0024] If all parent free lists are empty, then a contiguous memory segment is allocated from the unallocated space of the thread's local allocation area. The contiguous memory segment is then divided into multiple memory blocks of the target size according to the size corresponding to the target level. The first memory block of the multiple memory blocks of the target size is returned to the worker thread, and the remaining memory blocks of the target size are inserted into the free list of the target level in sequence.
[0025] In one optional embodiment, reading the thread-local allocation area from a thread-local storage variable, obtaining the requested size, performing a bitwise AND operation with a preset bitmask, performing a shift operation on the result to obtain the target level, and accessing the free list corresponding to the target level includes:
[0026] Read the starting address of the thread-local allocation area from the thread-local storage variable of the worker thread, access the metadata area of the thread-local allocation area according to the starting address, obtain the level configuration information of the free list with multi-level size classification from the metadata area, calculate the preset bitmask according to the minimum memory block size in the level configuration information, obtain the requested size in the memory allocation request, and perform a bitwise AND operation between the requested size and the preset bitmask to obtain the mask result.
[0027] Perform a bit-right shift operation on the mask result, setting the shift number to the binary number of bits corresponding to the minimum memory block size to obtain the level index value. Determine the level index value as the target level. Calculate the head address of the free list of the target level based on the starting address of the thread-local allocation area and the target level. Access the free list corresponding to the target level through the head address.
[0028] In one optional embodiment, when a worker thread releases a memory block, it uses sequence number bitwise operations to retrieve a buddy block and calculates the merge parameters. If a buddy block is found, the merged memory block is inserted into the parent free list, and the recursive merging process includes:
[0029] When a worker thread initiates a memory release request, it obtains the starting address of the memory block to be released, calculates the offset of the memory block to be released relative to the starting address of the thread's local allocation area, performs a bit right shift operation on the offset, sets the shift number to the number of bits corresponding to the size of the memory block to be released, obtains the memory block number, performs a bitwise inversion operation on the least significant bit of the memory block number to obtain the buddy number, calculates the offset of the buddy block based on the buddy number, adds the starting address of the thread's local allocation area to the buddy block offset to obtain the buddy address, and searches for a buddy block whose starting address matches the buddy address in the free list at the target level.
[0030] If a buddy block is found, it is removed from the free list at the target level. The sequence number of the memory block to be released is bitwise ANDed with the buddy number to obtain the merge sequence number. The merge start address is calculated based on the merge sequence number. The size of the memory block to be released is left-shifted by one bit to obtain the merge size. The merged memory block is constructed using the merge start address and the merge size. The merged memory block is inserted into the free list at the next higher level. The merged memory block is then used as the new memory block to be released and the process is recursively executed until no buddy block is found or the highest level is reached.
[0031] In an optional embodiment, the aggregation of memory blocks in the free list to be reclaimed according to address contiguity further includes:
[0032] Traverse the free list of the level to be reclaimed, extract the starting address of each memory block, and use the quicksort algorithm to sort the starting addresses in ascending order to generate an ordered sequence of memory blocks.
[0033] Starting from the first memory block in the memory block sequence, calculate the end address of the first memory block and determine whether the end address is equal to the start address of the next memory block;
[0034] If they are equal, the two memory blocks are merged into one aggregated memory block, the size of the aggregated memory block is updated, and the address continuity between the aggregated memory block and subsequent memory blocks is checked.
[0035] If they are not equal, the current aggregated memory block is added to the aggregation result queue, and a new aggregation process is started from the next memory block until the memory block sequence is traversed, and the maximized aggregated memory block set is obtained.
[0036] A second aspect of the present invention provides a multi-threaded memory management system that integrates massive pages and thread-local allocation areas, comprising:
[0037] The memory allocation unit is used to allocate a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool;
[0038] The thread mapping unit is used to divide the global memory pool into multiple thread-local allocation areas, and uses a consistent hashing algorithm to map worker threads to the thread-local allocation areas, storing the starting address of the thread-local allocation area in the thread-local storage variable.
[0039] The linked list construction unit is used to create a multi-level size-classified free linked list in the thread-local allocation area, wherein the free linked list with a level value greater than the target level is determined as the parent free linked list;
[0040] The memory allocation unit is used to read the thread-local allocation area from the thread-local storage variable when a worker thread requests memory allocation, map the requested size to the target level, and if the free list of the target level is empty, obtain the memory block from the parent free list and split it to the target level. If the parent free list is empty, then the memory block is split into blocks in batches from the unallocated space.
[0041] The memory release unit is used to retrieve the buddy block and calculate the merge parameters when the worker thread releases the memory block. If the buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively.
[0042] The memory reclamation unit is used to calculate the proportion of free memory based on a bitmap. If it exceeds a preset free threshold, it selects the level to be reclaimed based on the popularity score, aggregates the free memory blocks of the level to be reclaimed, and returns them to the global memory pool.
[0043] A third aspect of the present invention provides an electronic device, comprising:
[0044] processor;
[0045] Memory used to store processor-executable instructions;
[0046] The processor is configured to invoke instructions stored in the memory to execute the aforementioned method.
[0047] A fourth aspect of the present invention provides a computer-readable storage medium having stored thereon computer program instructions that, when executed by a processor, implement the aforementioned method.
[0048] In this embodiment of the invention, by requesting contiguous large pages of physical memory as a global memory pool, the physical memory fragmentation problem of traditional memory allocators is avoided, memory access locality is improved, and the TLB (Translation Lookaside Buffer) miss rate is significantly reduced, thereby improving memory access speed. The use of a thread-local allocation area structure and a consistent hashing algorithm for thread mapping effectively reduces inter-thread contention and lock contention, lowers synchronization overhead in multi-threaded environments, and improves concurrency performance. The use of thread-local storage variables to quickly locate the allocation area eliminates the global locking step in traditional methods, achieving a near-lock-free memory allocation process and significantly reducing memory allocation latency. The multi-level size-classified free list design and the buddy algorithm-based memory block merging mechanism effectively reduce memory fragmentation, improve memory utilization, and accelerate memory allocation and release operations. The bitmap-based free memory ratio calculation and heat-based memory reclamation strategy achieve intelligent dynamic adjustment of memory resources, optimizing overall system memory utilization efficiency while maintaining high performance. Attached Figure Description
[0049] Figure 1 This is a flowchart illustrating a multi-threaded memory management method that integrates big pages and thread-local allocation areas according to an embodiment of the present invention.
[0050] Figure 2 This is a flowchart for dynamic monitoring and reclamation of thread-local memory. Detailed Implementation
[0051] To make the objectives, technical solutions, and advantages of the embodiments of the present invention clearer, the technical solutions of the embodiments of the present invention will be clearly and completely described below with reference to the accompanying drawings. Obviously, the described embodiments are only some embodiments of the present invention, and not all embodiments. Based on the embodiments of the present invention, all other embodiments obtained by those skilled in the art without creative effort are within the scope of protection of the present invention.
[0052] The technical solution of the present invention will be described in detail below with reference to specific embodiments. These specific embodiments can be combined with each other, and the same or similar concepts or processes may not be described again in some embodiments.
[0053] Figure 1 This is a flowchart illustrating a multi-threaded memory management method that integrates big pages and thread-local allocation areas according to an embodiment of the present invention. Figure 1 As shown, the method includes:
[0054] Request a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool;
[0055] The global memory pool is divided into multiple thread-local allocation areas. A consistent hashing algorithm is used to map worker threads to the thread-local allocation areas, and the starting address of the thread-local allocation area is stored in the thread-local storage variable.
[0056] Within the thread-local allocation area, a multi-level size-classified free list is established, in which the free list with a level value greater than the target level is determined as the parent free list;
[0057] When a worker thread requests memory allocation, it reads the thread-local allocation area from the thread-local storage variable, maps the requested size to the target level, and if the free list at the target level is empty, it obtains memory blocks from the parent free list and splits them to the target level. If the parent free list is empty, it batch-splits memory blocks from the unallocated space.
[0058] When a worker thread releases a memory block, it uses bitwise operations to retrieve a buddy block and calculates the merge parameters. If a buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively.
[0059] The percentage of free memory is calculated based on a bitmap. If it exceeds a preset free threshold, a level to be reclaimed is selected based on a popularity score. The free memory blocks of the level to be reclaimed are then aggregated and returned to the global memory pool.
[0060] In one optional implementation, the global memory pool is divided into multiple thread-local allocation areas, and a consistent hashing algorithm is used to map worker threads to the thread-local allocation areas. Storing the starting address of the thread-local allocation area in a thread-local storage variable includes:
[0061] The size of a single thread-local allocation area is calculated based on the preset number of partitions. The address space of the global memory pool is divided into multiple thread-local allocation areas according to the calculated size. A unique partition identifier is assigned to each thread-local allocation area. The partition identifier is hashed to obtain the partition hash value. The partition hash value is mapped to different positions of the hash ring in numerical order to form a consistent hash ring. A mapping table between the partition identifier and the starting address of the thread-local allocation area is established for the consistent hash ring.
[0062] When a worker thread is initialized, its thread identifier is obtained, and a hash operation is performed on the thread identifier to obtain the thread hash value. The first partition hash value that is greater than or equal to the thread hash value is searched in a clockwise direction on the consistent hash ring. The corresponding partition identifier is determined based on the found partition hash value. The starting address of the thread local allocation area corresponding to the partition identifier is looked up through the mapping table, and the starting address is written into the worker thread's thread local storage variable to establish the binding relationship between the worker thread and the thread local allocation area.
[0063] In one specific implementation, during the partitioning and initialization of memory, the number of global memory pool partitions is pre-set to N, and this value is adjusted according to the actual application scenario and hardware configuration. Taking a specific application as an example, when the server is configured with a 32-core processor and each core supports 2 hardware threads, N is set to 128 to reserve sufficient local allocation space for each worker thread. The total capacity of the global memory pool is M bytes, for example, set to 4GB. Based on the preset number of partitions N and the total capacity of the global memory pool M, the size of the local allocation space for a single thread is calculated as S = M / N, that is, the size of each partition is 32MB.
[0064] The global memory pool starts at address BASE_ADDR, obtained using massive page allocation (MBPA), for example, BASE_ADDR is 0x10000000. Based on the calculated partition size S, the global memory pool is divided into N consecutive thread-local allocation regions. The starting address of the i-th partition is BASE_ADDR + i × S, where i ranges from 0 to N-1. For example, the starting address of the 0th partition is 0x10000000, the starting address of the 1st partition is 0x10000000 + 32MB = 0x12000000, and so on.
[0065] Assign a unique partition identifier ID to each thread's local allocation area. i The partition identifier can be directly used as the partition index i, or another unique identifier generation algorithm can be used. In this embodiment, the partition index i is directly used as the partition identifier, i.e., ID0=0, ID1=1, ..., ID1=1. N-1 =N-1. Identify the ID for each partition. i The partition hash value (HASH) is calculated using a hash function. i The hash function chosen is the 32-bit FNV-1a algorithm. The initial hash value is set to 2166136261. Each byte of the partition identifier is XORed and then multiplied by 16777619. For example, the hash value of the partition with ID0=0 is 2166136261, and the hash value of the partition with ID1=1 is 2180063277.
[0066] The calculated partition hash values are sorted according to their numerical value to construct a ring-shaped consistent hash ring. On the hash ring, the partition hash values are arranged in ascending order, with the next node after the largest hash value being the smallest hash value, forming a closed loop. For example, the sorted hash value sequence is: 1234567890, 1876543210, 2180063277, 2649828418, ..., 3987654321, forming a ring structure.
[0067] Construct a mapping table (MAP) between partition identifiers and the starting addresses of thread-local allocation regions. This mapping table is implemented using a hash table. For each partition identifier ID... i and the corresponding partition start address ADDR i =BASE_ADDR+i×S, create key-value pairs in the mapping table. <ID i ADDR i For example, <0, 0x10000000>, <1, 0x12000000>, <2, 0x14000000>, etc. Furthermore, a mapping relationship between partition hash values and partition identifiers needs to be established to facilitate finding the corresponding partition from its position on the hash ring.
[0068] During the worker thread binding process, when a new worker thread TID starts and initializes, the thread identifier THREAD_ID is obtained. This identifier is assigned by the operating system, for example, THREAD_ID=12345. The same hash function as the partition identifier is applied to the thread identifier to calculate the thread hash value THREAD_HASH. For example, applying the FNV-1a hash algorithm to THREAD_ID=12345 yields THREAD_HASH=2578964213.
[0069] On the constructed consistent hash ring, starting from the thread hash value THREAD_HASH, the search proceeds clockwise to find the first partition hash value PART_HASH that is greater than or equal to THREAD_HASH. If no partition hash value greater than THREAD_HASH exists, the first partition hash value on the hash ring is used. For example, if THREAD_HASH = 2578964213, the search on the hash ring yields PART_HASH = 2649828418.
[0070] Based on the found partition hash value PART_HASH, determine the corresponding partition identifier PART_ID. For example, the partition identifier corresponding to PART_HASH=2649828418 is PART_ID=42. Using the previously established mapping table MAP, query the starting address PART_ADDR of the thread-local allocation area corresponding to the partition identifier PART_ID. For example, the starting address corresponding to PART_ID=42 is PART_ADDR=0x10000000+42×32MB=0x64000000.
[0071] The retrieved thread-local allocation region starting address PART_ADDR is written to the worker thread's thread-local storage variable TLS_ADDR. Thread-local storage is a mechanism provided by the operating system that allows each thread to have its own independent copy of the variable. PART_ADDR is stored in a predefined key-value pair by calling pthread_setspecific or a similar thread-local storage API. For example, pthread_setspecific(ALLOC_AREA_KEY, (void*)PART_ADDR).
[0072] To optimize memory access performance, the end address of the allocated region (END_ADDR = PART_ADDR + S) and the current allocation pointer (CURRENT_PTR) are recorded simultaneously in thread-local storage. Initially, CURRENT_PTR = PART_ADDR. For example, if PART_ADDR = 0x64000000, the corresponding END_ADDR = 0x64000000 + 32MB = 0x66000000, and the initial value of CURRENT_PTR is 0x64000000.
[0073] Through the above steps, a binding relationship is established between the worker thread and its dedicated thread-local allocation area. When a thread needs to allocate memory, it allocates directly from its thread-local allocation area without the need for global lock protection, thus significantly reducing memory allocation contention in a multi-threaded environment. For example, when a thread needs to allocate 1KB of memory, it checks whether CURRENT_PTR + 1KB exceeds END_ADDR. If it does not exceed END_ADDR, it returns CURRENT_PTR as the allocation result and increments CURRENT_PTR by 1KB; if it exceeds END_ADDR, it needs to handle the insufficient memory situation.
[0074] In one optional implementation, when a worker thread requests memory allocation, the thread-local allocation area is read from the thread-local storage variable, the requested size is mapped to the target level, and if the free list at the target level is empty, a memory block is obtained from the parent free list and split to the target level. If all parent free lists are empty, then the batch splitting of memory blocks from unallocated space includes:
[0075] When a worker thread initiates a memory allocation request, it reads the thread-local allocation area from the thread-local storage variable, obtains the requested size, performs a bitwise AND operation with the preset bitmask, performs a shift operation on the result to obtain the target level, and accesses the free list corresponding to the target level.
[0076] If the target level free list is empty, traverse the parent free list, find the first non-empty parent free list, extract a memory block from the first non-empty parent free list as the memory block to be split, calculate the difference between the size of the memory block to be split and the size corresponding to the target level, determine the number of splits based on the difference, perform the split operation corresponding to the number of splits on the memory block to be split, return the target size memory block obtained after splitting to the worker thread, and insert the intermediate size memory block generated during the splitting process into the free list of the corresponding level.
[0077] If all parent free lists are empty, then a contiguous memory segment is allocated from the unallocated space of the thread's local allocation area. The contiguous memory segment is then divided into multiple memory blocks of the target size according to the size corresponding to the target level. The first memory block of the multiple memory blocks of the target size is returned to the worker thread, and the remaining memory blocks of the target size are inserted into the free list of the target level in sequence.
[0078] In one implementation, when a worker thread initiates a memory allocation request, it needs to read the address of the Thread Local Allocation Block (TLAB) from the Thread Local Storage (TLS) variable. Each worker thread is allocated an independent TLAB upon creation to reduce inter-thread contention and improve memory allocation efficiency. The TLAB contains multiple levels of free list arrays, each list corresponding to a memory block of a specific size, and a contiguous unallocated memory space.
[0079] After obtaining the TLAB, the target level is determined based on the requested memory size. A bitwise AND operation is performed between the requested size and a preset bitmask, for example, using a mask of 0xFFF0. After removing low-order biases, the corresponding level index is quickly located by right-shifting by 4 bits and looking up a table. Assume the memory allocation system supports 16 levels, from level 0 to level 15, corresponding to memory blocks ranging from 16 bytes to 512KB in size.
[0080] For example, when requesting the allocation of 38 bytes of memory, a bitwise AND operation is performed between 38 and 0xFFF0 to obtain 32. 32 is then shifted right by 4 bits to obtain 2. The lookup table shows that the level is 2, and the corresponding memory block size is 32 bytes. This mapping method is completed in O(1) time, greatly improving the allocation efficiency.
[0081] Access the free list corresponding to the target level in the TLAB. The free list is implemented using a singly linked list, where each node contains a pointer to the next node and the actual available memory space. If the free list at the target level is not empty, directly remove a memory block from the head of the list and return it to the worker thread to complete the allocation.
[0082] If the free list at the target level is empty, a memory block needs to be obtained from the parent free list and split. Starting from the target level, traverse the free list array upwards, searching for the first non-empty parent list. Once a non-empty parent list is found, a memory block is taken from the head of the list as the memory block to be split.
[0083] Taking a request for 32 bytes but an empty level 2 free list as an example, assume the first non-empty parent list is level 4, corresponding to a memory block size of 128 bytes. The size difference between the memory block to be split (128 bytes) and the target level memory block (32 bytes) is calculated to be 96 bytes, indicating that two splits are needed: first, split the 128-byte block into two 64-byte blocks; second, split a single 64-byte block into two 32-byte blocks.
[0084] During a split operation, the memory block to be split is equally divided into two parts. One part is reserved for further splitting or returned to the worker thread, and the other part is inserted into the free list of the corresponding level. In the example above, after the first split, a 64-byte block is inserted into the free list of level 3; after the second split, a 32-byte block is inserted into the free list of level 2, and the other 32-byte block is returned to the worker thread.
[0085] When all parent free lists are empty, memory needs to be allocated from the unallocated space of the TLAB. The unallocated space of the TLAB is a contiguous memory region identified by its start and end addresses. The size of the unallocated space is checked to see if it is sufficient for batch allocation. If insufficient, a new memory block needs to be requested from the global memory manager to expand the TLAB.
[0086] A contiguous memory segment is allocated from the unallocated space, with a length equal to the batch allocation factor multiplied by the target level memory block size. The batch allocation factor is dynamically adjusted based on system load and memory usage patterns, typically a value between 4 and 16. For example, if the target level is 2 (corresponding to 32 bytes) and the batch allocation factor is 8, a contiguous memory segment of 256 bytes will be allocated.
[0087] The contiguous memory segment is divided into multiple memory blocks of the same size according to the memory block size corresponding to the target level. In this embodiment, the 256-byte memory segment is divided into eight 32-byte memory blocks. The first memory block is returned to the worker thread, and the remaining seven memory blocks are sequentially inserted into the free list of the target level for use by subsequent memory allocation requests of the same size.
[0088] This hierarchical memory management approach, combined with thread-local allocation technology, effectively reduces inter-thread contention and memory fragmentation, and improves the efficiency of memory allocation and reclamation. It is particularly suitable for memory-intensive application scenarios in high-concurrency environments.
[0089] In one optional implementation, the thread-local allocation area is read from the thread-local storage variable, the requested size is obtained, and a bitwise AND operation is performed with a preset bitmask. The result of the operation is then shifted to obtain the target level. Accessing the free list corresponding to the target level includes:
[0090] Read the starting address of the thread-local allocation area from the thread-local storage variable of the worker thread, access the metadata area of the thread-local allocation area according to the starting address, obtain the level configuration information of the free list with multi-level size classification from the metadata area, calculate the preset bitmask according to the minimum memory block size in the level configuration information, obtain the requested size in the memory allocation request, and perform a bitwise AND operation between the requested size and the preset bitmask to obtain the mask result.
[0091] Perform a bit-right shift operation on the mask result, setting the shift number to the binary number of bits corresponding to the minimum memory block size to obtain the level index value. Determine the level index value as the target level. Calculate the head address of the free list of the target level based on the starting address of the thread-local allocation area and the target level. Access the free list corresponding to the target level through the head address.
[0092] In one specific implementation, at the start of the memory allocation process, the worker thread obtains the starting address of its thread-local allocation area by accessing a thread-local storage variable. Thread-local storage is a special storage mechanism where each thread has its own independent copy of the variable, avoiding data contention between threads. For example, when the worker thread with thread identifier 1024 needs to allocate memory, it will look up the variable with the key "THREAD_LOCAL_ALLOCATOR" in its thread-local storage. The value stored in this variable is the starting address of the thread-specific thread-local allocation area, assuming the address is 0x7FFFD8000000.
[0093] After obtaining the starting address of the thread-local allocation area, it is necessary to access the metadata area of the allocation area. The metadata area is located at a specific offset position relative to the thread-local allocation area, usually at the beginning. Assuming that the offset of the metadata area relative to the starting position of the thread-local allocation area is 64 bytes, then the starting address of the metadata area is 0x7FFFD8000040. The metadata area contains various information needed to manage the thread-local allocation area, an important part of which is the level configuration information of the free list with multi-level size classification.
[0094] From the metadata area, read the level configuration information, which defines the range of memory block sizes managed by the free lists at different levels. Level configuration information typically includes the minimum memory block size, the number of levels, and the memory block size for each level. Assume the minimum memory block size is 16 bytes, the number of levels is 32, and the memory block size for each level increases according to a specific rule.
[0095] A preset bitmask is calculated based on the minimum memory block size. This preset bitmask helps determine the appropriate level to which the requested size should belong. Assuming the minimum memory block size is 16 bytes, the preset bitmask is set to hexadecimal 0xF, which is binary 1111. The mask will be used in subsequent bitwise operations to help determine the appropriate level index.
[0096] When a memory allocation request is received, the size of the memory specified in the request, i.e., the request size, needs to be obtained. Assume the current request allocates 37 bytes of memory. Since actual memory allocation usually involves alignment, the 37-byte request is adjusted upwards to the nearest alignment boundary, such as adjusting it to 48 bytes.
[0097] Perform a bitwise AND operation between the requested size and the preset bitmask to obtain the mask result. Perform a bitwise AND operation between the adjusted requested size of 48 bytes and the preset bitmask 0xF, and the result is 0, indicating that the requested size is an integer multiple of the minimum memory block size.
[0098] The mask result is then right-shifted by a number of bits equal to the number of bits corresponding to the minimum memory block size. The minimum memory block size is 16 bytes, which requires 4 bits in its binary representation; therefore, the shift number is 4. Right-shifting the adjusted request size of 48 by 4 bits yields the result 3.
[0099] The right shift result of 3 is the level index value, which is determined as the target level. This means that the requested memory size should be allocated from the free list of level 3, and the memory block size managed by this level is suitable for storing 48 bytes of data.
[0100] Based on the starting address of the thread-local allocation area and the target level, calculate the head address of the free list at the target level. Assume each free list head occupies 16 bytes of space, and the free list head region is located after the metadata region, with a starting offset of 256 bytes. Then, the head address of the level 3 free list is calculated as: 0x7FFFD8000000 (starting address of the thread-local allocation area) + 256 (starting offset of the free list head region) + 3 × 16 (offset of level 3 in the free list head region) = 0x7FFFD8000130.
[0101] Access the free list corresponding to the target level through the head address of the linked list. The free list is usually a singly or doubly linked list structure, and the head of the list contains a pointer to the first free memory block. Assuming that there is an available free memory block in the level 3 free list with the address 0x7FFFD8001000, then 48 bytes of memory space are obtained from that address and allocated to the requester.
[0102] If the free list at the target level is empty, meaning there are no available free memory blocks, then a memory block needs to be split from a higher-level free list, or a new memory page needs to be requested from the operating system. Suppose a new large page needs to be requested from the operating system. After successful request, a 2MB large page at address 0x7FFFD9000000 is obtained. This large page is then split into multiple 48-byte memory blocks, and these memory blocks are linked to the third-level free list. From this list, a memory block is allocated to the requester.
[0103] During the actual allocation process, it is also necessary to update the statistics of the thread-local allocation area, such as the total amount of allocated memory and the amount of remaining available memory, to facilitate subsequent memory management decisions. Simultaneously, to improve performance, prefetching and batch allocation strategies are implemented to reduce the frequency of operating system calls.
[0104] The above technical solutions integrate large page technology and thread-local allocation area mechanism to achieve an efficient multi-threaded memory management method, effectively reducing competition between threads and improving memory allocation efficiency, making it particularly suitable for high-concurrency multi-threaded application scenarios.
[0105] In one optional implementation, when a worker thread releases a memory block, it uses sequence number bitwise operations to retrieve a buddy block and calculates the merge parameters. If a buddy block is found, the merged memory block is inserted into the parent free list, and the recursive merging process includes:
[0106] When a worker thread initiates a memory release request, it obtains the starting address of the memory block to be released, calculates the offset of the memory block to be released relative to the starting address of the thread's local allocation area, performs a bit right shift operation on the offset, sets the shift number to the number of bits corresponding to the size of the memory block to be released, obtains the memory block number, performs a bitwise inversion operation on the least significant bit of the memory block number to obtain the buddy number, calculates the offset of the buddy block based on the buddy number, adds the starting address of the thread's local allocation area to the buddy block offset to obtain the buddy address, and searches for a buddy block whose starting address matches the buddy address in the free list at the target level.
[0107] If a buddy block is found, it is removed from the free list at the target level. The sequence number of the memory block to be released is bitwise ANDed with the buddy number to obtain the merge sequence number. The merge start address is calculated based on the merge sequence number. The size of the memory block to be released is left-shifted by one bit to obtain the merge size. The merged memory block is constructed using the merge start address and the merge size. The merged memory block is inserted into the free list at the next higher level. The merged memory block is then used as the new memory block to be released and the process is recursively executed until no buddy block is found or the highest level is reached.
[0108] In one implementation, when a worker thread releases memory, it obtains the starting address of the memory block to be released. For example, a worker thread needs to release a memory block located at address 0x7FFFD8001000, with a size of 64 bytes. To determine the location of the memory block in the thread-local allocation area, it is necessary to calculate the offset of the memory block relative to the starting address of the thread-local allocation area. Assuming the starting address of the thread-local allocation area is 0x7FFFD8000000, the offset of the memory block to be released is 0x1000, or 4096.
[0109] The calculated offset is right-shifted by a number of bits equal to the binary number of bits corresponding to the size of the memory block to be freed. The memory block to be freed is 64 bytes, and its binary representation requires 6 bits, therefore the shift number is 6. Shifting the offset 4096 right by 6 bits yields 64, which is the memory block number, indicating that the memory block is the 64th 64-byte memory block in the thread-local allocation area.
[0110] The buddy number is obtained by bitwise inverting the least significant bit of the memory block number. For example, memory block number 64 is represented as 1000000 with a least significant bit of 0. Inverting this bit gives a least significant bit of 1, resulting in buddy number 65, represented as 1000001. The buddy number is used to identify the buddy block being merged with the currently unreleased memory block.
[0111] The offset of the buddy block is calculated based on the buddy number. Multiplying the buddy number 65 by the memory block size 64 gives an offset of 4160. Adding the thread-local allocation region starting address 0x7FFFD8000000 to the buddy block offset 4160 gives the buddy address 0x7FFFD8001040, which is the starting address of the buddy block.
[0112] Search the free list at the target level for a buddy block whose starting address matches the buddy address. The target level is the level that manages 64-byte memory blocks, let's say level 4. Traverse the free list at level 4, checking the starting address of each free memory block to see if a memory block with a starting address of 0x7FFFD8001040 exists. If such a memory block exists, it means a buddy block for the memory block to be freed has been found.
[0113] If a buddy block is found, that is, a memory block with a starting address of 0x7FFFD8001040 is found in the free list at level 4, then the buddy block needs to be removed from the free list. The removal operation involves adjusting the list pointers, making the next pointer of the node preceding the buddy block point to the next node of the buddy block, thereby removing the buddy block from the list.
[0114] A bitwise AND operation is performed between the sequence number of the memory block to be freed and the buddy sequence number to obtain the merge sequence number. For example, a bitwise AND operation is performed between memory block sequence number 64 and buddy sequence number 65, resulting in 64. The merge sequence number is used to determine the location of the merged memory block.
[0115] The merge start address is calculated based on the merge sequence number. The merge sequence number 64 is multiplied by the memory block size 64 to obtain an offset of 4096. The thread local allocation area start address 0x7FFFD8000000 is added to the offset to obtain the merge start address 0x7FFFD8001000.
[0116] The size of the memory block to be freed is shifted left by one bit to obtain the merged size. Shifting the memory block size 64 left by one bit results in a merged size of 128. Shifting left by one bit is equivalent to multiplying the original size by 2, meaning the merged memory block is twice the size of the original memory block.
[0117] Construct a merged memory block using the merge start address and merge size. Using the merge start address 0x7FFFD8001000 and a merge size of 128 bytes, construct a new merged memory block. A memory block represents a larger contiguous memory region formed by merging the original memory block to be freed with its buddy block.
[0118] The merged memory block is inserted into the free list corresponding to the parent level. The parent level is the level that manages 128-byte memory blocks, let's assume it's level 5. Specifically, inserting the merged memory block into the free list of level 5 involves adjusting the list pointers so that the merged memory block becomes part of the list.
[0119] Using the merged memory block as the new memory block to be freed, the above process is recursively executed. Now, the merged memory block becomes the new memory block to be freed, with a starting address of 0x7FFFD8001000 and a size of 128 bytes. The above calculation process is executed again to calculate the new memory block sequence number, buddy sequence number, and buddy address, and to search for a matching buddy block in the free list at level 5.
[0120] The recursive process continues until no buddy block is found or the highest level is reached. For example, if no buddy block at address 0x7FFFD8001080 is found in the free list at level 5, the recursive process terminates, and the merged memory block (starting at address 0x7FFFD8001000, size 128 bytes) is left in the free list at level 5. Alternatively, if buddy blocks are continuously found and merged, eventually reaching the highest level, such as the level managing 2MB memory blocks, the recursive process will also terminate.
[0121] This buddy system algorithm enables efficient management of memory blocks in the thread-local allocation area, allowing for dynamic splitting and merging of memory blocks. When multiple small memory blocks are released, the system automatically merges them into larger contiguous memory blocks, reducing memory fragmentation and improving memory utilization. Furthermore, since each worker thread has its own independent thread-local allocation area, memory release operations in a multi-threaded environment are performed in parallel, avoiding global lock contention and improving system concurrency performance.
[0122] In one optional implementation, the free memory percentage is calculated based on a bitmap. If it exceeds a preset free threshold, a level to be reclaimed is selected based on a popularity score. The free memory blocks of the reclaimed level are then aggregated and returned to the global memory pool, including:
[0123] In the metadata area of the thread-local allocation area, a free memory bitmap is maintained. Each bit in the bitmap corresponds to the occupancy status of a memory block of the smallest granularity. Each time memory is allocated or released, the corresponding position in the bitmap is set or cleared.
[0124] By performing segmented parallel bit counting operations on the bitmap, the number of free bits is counted, and the percentage of free memory is calculated based on the number of free bits and the total number of bits in the bitmap.
[0125] Maintain an access timestamp queue for each level of the free list. Each time access occurs, the current timestamp is enqueued. Calculate the access density of each level within a preset time window. Weight the access density with the memory block size to obtain a popularity score.
[0126] Determine if the percentage of free memory exceeds the preset free threshold. If it does, select the level with the lowest popularity score as the level to be reclaimed.
[0127] Memory blocks in the free list to be reclaimed are aggregated according to address contiguity. Multiple memory blocks with contiguity are merged into an aggregated memory block, and the target level of the aggregated memory block in the global memory pool is calculated.
[0128] The aggregated memory blocks are pushed in batches to the recycling buffer of the global memory pool. Based on the background thread of the global memory pool, the insertion operation is completed asynchronously, and the bit state of the corresponding area in the bitmap is updated.
[0129] In one implementation, a free memory bitmap is constructed and maintained in the metadata region of the thread-local allocation area. Each thread-local allocation area contains a fixed-size memory, such as 64MB, divided into several memory blocks according to the smallest granularity (e.g., 64 bytes). Each bit in the bitmap corresponds one-to-one with a memory block of the smallest granularity; a bit value of 0 indicates that the memory block is free, and a value of 1 indicates that the memory block is occupied. The bitmap is stored in a fixed location in the metadata region, implemented using a contiguous byte array. When the application calls the memory allocation function, the allocator finds a sufficiently large free memory block and sets the corresponding bit in the bitmap from 0 to 1; when the application releases memory, the allocator clears the corresponding bit in the bitmap from 1 to 0.
[0130] To efficiently calculate the free memory percentage, a segmented parallel bit counting algorithm is employed. The bitmap is divided into groups of 256 bits each, and for each group, a lookup table or built-in instruction (such as POPCNT for x86 architecture) is used to count the number of bits with a value of 0 in parallel. For a 64MB memory region and a minimum granularity of 64 bytes, the bitmap size is 128KB, containing 1,048,576 bits. The total number of free bits is obtained by summing the results of each group. The formula for calculating the free memory percentage is: Free memory percentage = Number of free bits ÷ Total number of bits in the bitmap × 100%.
[0131] To assess the usage frequency of memory blocks at each level, a circular queue of access timestamps is maintained for the free list of each memory level. Memory levels are divided according to memory block size; for example, level 0 corresponds to 64 bytes, level 1 to 128 bytes, and so on. Each queue has a fixed length of 100 and stores the timestamps of the most recent 100 accesses to memory at that level. When an application requests allocation or access to memory at a certain level, the current system time (millisecond precision) is pushed into the corresponding level's timestamp queue. If the queue is full, the oldest record is overwritten.
[0132] The popularity score is calculated based on a weighted average of access density and memory block size within a preset time window (e.g., the last 5 seconds). The number of accesses within the time window is calculated, i.e., the number of records in the queue with a timestamp greater than (current time - 5000ms). Access density equals the number of accesses divided by the time window size. The popularity score formula is: Popularity Score = Access Density × Memory Block Size × Weighting Factor. The weighting factor is adjusted according to the application scenario; generally, larger memory blocks have a slightly higher weight because a single reclamation of a large memory block releases more space.
[0133] The condition for triggering memory reclamation is that the percentage of free memory exceeds a preset threshold, such as 25%. When the calculated percentage of free memory exceeds the threshold, the level with the lowest popularity score is selected as the reclamation target. For example, if the calculated popularity score for level 0 is 0.8, level 1 is 1.2, and level 2 is 0.5, then level 2 is selected as the level to be reclaimed.
[0134] For the selected reclaiming level, memory blocks are extracted from its free list and aggregated according to address contiguousness. The memory blocks are sorted by their starting addresses, and the sorted list is traversed, checking if adjacent memory blocks have contiguous addresses. If the ending address of memory block A equals the starting address of memory block B, they are merged into a larger memory block. Through iterative merging, a series of aggregated memory blocks are obtained, each corresponding to a higher-level memory level.
[0135] Calculate the target level of the global memory pool corresponding to the aggregated memory block. The global memory pool is divided into levels according to powers of 2, for example, level 10 corresponds to 1024 bytes, and level 11 corresponds to 2048 bytes. Calculate the target level corresponding to the aggregated memory block based on its size, such as an aggregated block of 1536 bytes corresponding to level 11 (2048 bytes).
[0136] The aggregated memory blocks are pushed in batches to the global memory pool's reclamation buffer. The reclamation buffer is a thread-safe queue structure that holds multiple memory blocks to be reclaimed. The background thread of the global memory pool periodically (e.g., every 100ms) checks the reclamation buffer, inserts the memory blocks in it into the corresponding level of the global free list, and updates the bit status of the corresponding area in the thread-local allocation bitmap, remarking these positions as reclaimed (setting them to 0).
[0137] Through the above technologies, the memory management system can detect and reclaim excessive free memory in the thread-local allocation area in a timely manner, reduce memory fragmentation, improve overall memory utilization, and at the same time avoid frequent reclamation of hot memory levels, thus maintaining good application performance.
[0138] In one alternative implementation, the aggregation of memory blocks in the free list to be reclaimed according to address contiguity further includes:
[0139] Traverse the free list of the level to be reclaimed, extract the starting address of each memory block, and use the quicksort algorithm to sort the starting addresses in ascending order to generate an ordered sequence of memory blocks.
[0140] Starting from the first memory block in the memory block sequence, calculate the end address of the first memory block and determine whether the end address is equal to the start address of the next memory block;
[0141] If they are equal, the two memory blocks are merged into one aggregated memory block, the size of the aggregated memory block is updated, and the address continuity between the aggregated memory block and subsequent memory blocks is checked.
[0142] If they are not equal, the current aggregated memory block is added to the aggregation result queue, and a new aggregation process is started from the next memory block until the memory block sequence is traversed, and the maximized aggregated memory block set is obtained.
[0143] In one specific implementation, the method is triggered when the system detects that the free list at a specific reclamation level needs to be merged for optimization. The free list stores information about multiple memory blocks, each containing its start address and size. To achieve efficient merging, the memory blocks need to be sorted by their start addresses, and adjacent memory blocks are identified and merged.
[0144] Specifically, the free list of memory blocks to be reclaimed is traversed, and key information for each memory block is extracted. For each node in the list, the starting address and size information of the memory block are obtained, and a memory block description structure is constructed. The structure contains two main fields: starting address and memory block size. The starting address, in bytes, represents the starting position of the memory block in physical or virtual memory; the memory block size, also in bytes, represents the total amount of available space in the memory block.
[0145] After extracting all memory block information, the quicksort algorithm is used to sort the memory blocks in ascending order by their starting addresses. Quicksort is implemented by selecting a pivot element, dividing the sequence into two parts: those less than the pivot and those greater than the pivot, and recursively sorting these two subsequences. In this implementation, the comparison element is the starting address of the memory block. After sorting, a sequence of memory blocks arranged in ascending order of their starting addresses is obtained, laying the foundation for subsequent merge operations.
[0146] After sorting, the memory block merging phase begins. Processing starts with the first memory block in the sorted sequence, which is set as the current aggregate block. The end address of the current aggregate block is calculated by adding the memory block size to the start address. It is then checked whether the end address is equal to the start address of the next memory block in the sequence. If they are equal, the two memory blocks are contiguous in the address space and are merged.
[0147] When two memory blocks are found to be merging, a merge operation is performed, keeping the starting address of the current merged block unchanged, and updating its size to the original size plus the size of the next memory block. This forms a larger merged memory block. The end address of the new merged block is calculated and compared with the starting address of the next memory block, and the merging process is repeated until a non-contiguous memory block is encountered or the end of the sequence is reached.
[0148] When a memory block that is not contiguous with the current aggregate block is encountered, the current aggregate block is added to the final aggregation result queue, and the next non-contiguous memory block is set as the new current aggregate block, starting a new round of merging. Repeat the above steps until all memory blocks in the sequence have been processed.
[0149] For example, suppose the free list with reclamation level 2 contains the following memory blocks: memory block 1, starting address 0x1000, size 4096 bytes; memory block 2, starting address 0x3000, size 4096 bytes; memory block 3, starting address 0x2000, size 4096 bytes; memory block 4, starting address 0x6000, size 8192 bytes; memory block 5, starting address 0x5000, size 4096 bytes.
[0150] After performing quicksort, the memory block sequence becomes: memory block 1, starting address 0x1000, size 4096 bytes; memory block 3, starting address 0x2000, size 4096 bytes; memory block 2, starting address 0x3000, size 4096 bytes; memory block 5, starting address 0x5000, size 4096 bytes; memory block 4, starting address 0x6000, size 8192 bytes.
[0151] Entering the merging phase, memory block 1 is processed, with an end address of 0x1000 + 4096 = 0x2000, which is equal to the starting address of the next memory block, so it can be merged. The merged aggregate block starts at address 0x1000 and has a size of 4096 + 4096 = 8192 bytes.
[0152] Continuing to process the aggregate block, the ending address is 0x1000 + 8192 = 0x3000, which is the same as the starting address of the next memory block, so it can be merged. The merged aggregate block starts at address 0x1000 and has a size of 8192 + 4096 = 12288 bytes.
[0153] Continuing to process the aggregate block, the end address is 0x1000 + 12288 = 0x4000, which is not equal to the starting address of the next memory block 0x5000, so it cannot be merged. Add the current aggregate block (starting address 0x1000, size 12288 bytes) to the results queue.
[0154] Processing begins with memory block 5 as the new aggregate block, ending at address 0x5000 + 4096 = 0x6000, which is the same as the starting address of the next memory block, allowing them to be merged. The merged aggregate block starts at address 0x5000 and has a size of 4096 + 8192 = 12288 bytes.
[0155] There are no more memory blocks in the sequence, so the last aggregate block (starting address 0x5000, size 12288 bytes) is added to the results queue.
[0156] The final aggregation result is two larger memory blocks: aggregation block 1, starting at address 0x1000, with a size of 12288 bytes (merging the original memory blocks 1, 3, and 2); and aggregation block 2, starting at address 0x5000, with a size of 12288 bytes (merging the original memory blocks 5 and 4).
[0157] In this way, the original five scattered memory blocks are optimized and merged into two large memory blocks, while maintaining the same total memory size (24,576 bytes). However, the number of memory blocks is reduced, effectively alleviating memory fragmentation and improving memory allocation efficiency. This is especially important in multi-threaded environments, as it reduces memory contention between threads and increases the utilization of large memory pages.
[0158] In this embodiment, the algorithm is executed periodically or under specific triggering conditions, such as increased memory pressure or when the number of free memory blocks exceeds a threshold. The time complexity of the method is mainly determined by quicksort, averaging O(n log n), where n is the number of memory blocks. When implemented in a multi-threaded environment, concurrent access control of the memory linked list needs to be considered to ensure the atomicity and consistency of sorting and merging operations.
[0159] This invention's embodiment of a multi-threaded memory management system integrating huge pages and thread-local allocation areas includes:
[0160] The memory allocation unit is used to allocate a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool;
[0161] The thread mapping unit is used to divide the global memory pool into multiple thread-local allocation areas, and uses a consistent hashing algorithm to map worker threads to the thread-local allocation areas, storing the starting address of the thread-local allocation area in the thread-local storage variable.
[0162] The linked list construction unit is used to create a multi-level size-classified free linked list in the thread-local allocation area, wherein the free linked list with a level value greater than the target level is determined as the parent free linked list;
[0163] The memory allocation unit is used to read the thread-local allocation area from the thread-local storage variable when a worker thread requests memory allocation, map the requested size to the target level, and if the free list of the target level is empty, obtain the memory block from the parent free list and split it to the target level. If the parent free list is empty, then the memory block is split into blocks in batches from the unallocated space.
[0164] The memory release unit is used to retrieve the buddy block and calculate the merge parameters when the worker thread releases the memory block. If the buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively.
[0165] The memory reclamation unit is used to calculate the proportion of free memory based on a bitmap. If it exceeds a preset free threshold, it selects the level to be reclaimed based on the popularity score, aggregates the free memory blocks of the level to be reclaimed, and returns them to the global memory pool.
[0166] A third aspect of the present invention provides an electronic device, comprising:
[0167] processor;
[0168] Memory used to store processor-executable instructions;
[0169] The processor is configured to invoke instructions stored in the memory to execute the aforementioned method.
[0170] A fourth aspect of the present invention provides a computer-readable storage medium having stored thereon computer program instructions that, when executed by a processor, implement the aforementioned method.
[0171] This invention can be a method, apparatus, system, and / or computer program product. The computer program product may include a computer-readable storage medium having computer-readable program instructions loaded thereon for performing various aspects of the invention.
[0172] Finally, it should be noted that the above embodiments are only used to illustrate the technical solutions of the present invention, and not to limit them; although the present invention has been described in detail with reference to the foregoing embodiments, those skilled in the art should understand that modifications can still be made to the technical solutions described in the foregoing embodiments, or equivalent substitutions can be made to some or all of the technical features; and these modifications or substitutions do not cause the essence of the corresponding technical solutions to deviate from the scope of the technical solutions of the embodiments of the present invention.
Claims
1. A multi-threaded memory management method that integrates massive pages and thread-local allocation areas, characterized in that, include: Request a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool; The global memory pool is divided into multiple thread-local allocation areas. A consistent hashing algorithm is used to map worker threads to the thread-local allocation areas, and the starting address of the thread-local allocation area is stored in the thread-local storage variable. Within the thread-local allocation area, a multi-level size-classified free list is established, in which the free list with a level value greater than the target level is determined as the parent free list; When a worker thread requests memory allocation, it reads the thread-local allocation area from the thread-local storage variable, maps the requested size to the target level, and if the free list at the target level is empty, it obtains memory blocks from the parent free list and splits them to the target level. If the parent free list is empty, it batch-splits memory blocks from the unallocated space. When a worker thread releases a memory block, it uses bitwise operations to retrieve a buddy block and calculates the merge parameters. If a buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively. The free memory percentage is calculated based on a bitmap. If it exceeds a preset free threshold, a level to be reclaimed is selected based on a popularity score. The free memory blocks of the reclaimed level are then aggregated and returned to the global memory pool, including: In the metadata area of the thread-local allocation area, a free memory bitmap is maintained. Each bit in the bitmap corresponds to the occupancy status of a memory block of the smallest granularity. Each time memory is allocated or released, the corresponding position in the bitmap is set or cleared. By performing segmented parallel bit counting operations on the bitmap, the number of free bits is counted, and the percentage of free memory is calculated based on the number of free bits and the total number of bits in the bitmap. Maintain an access timestamp queue for each level of the free list. Each time access occurs, the current timestamp is enqueued. Calculate the access density of each level within a preset time window. Weight the access density with the memory block size to obtain a popularity score. Determine if the percentage of free memory exceeds the preset free threshold. If it does, select the level with the lowest popularity score as the level to be reclaimed. Memory blocks in the free list to be reclaimed are aggregated according to address contiguity. Multiple memory blocks with contiguity are merged into an aggregated memory block, and the target level of the aggregated memory block in the global memory pool is calculated. The aggregated memory blocks are pushed in batches to the recycling buffer of the global memory pool. Based on the background thread of the global memory pool, the insertion operation is completed asynchronously, and the bit state of the corresponding area in the bitmap is updated.
2. The method according to claim 1, characterized in that, The global memory pool is divided into multiple thread-local allocation areas. A consistent hashing algorithm is used to map worker threads to their respective thread-local allocation areas. The starting address of each thread-local allocation area is stored in a thread-local storage variable, including: The size of a single thread-local allocation area is calculated based on the preset number of partitions. The address space of the global memory pool is divided into multiple thread-local allocation areas according to the calculated size. A unique partition identifier is assigned to each thread-local allocation area. The partition identifier is hashed to obtain the partition hash value. The partition hash value is mapped to different positions of the hash ring in numerical order to form a consistent hash ring. A mapping table between the partition identifier and the starting address of the thread-local allocation area is established for the consistent hash ring. When a worker thread is initialized, its thread identifier is obtained, and a hash operation is performed on the thread identifier to obtain the thread hash value. The first partition hash value that is greater than or equal to the thread hash value is searched in a clockwise direction on the consistent hash ring. The corresponding partition identifier is determined based on the found partition hash value. The starting address of the thread local allocation area corresponding to the partition identifier is looked up through the mapping table, and the starting address is written into the worker thread's thread local storage variable to establish the binding relationship between the worker thread and the thread local allocation area.
3. The method according to claim 1, characterized in that, When a worker thread requests memory allocation, it reads the thread-local allocation area from the thread-local storage variable, maps the requested size to the target level, and if the free list at the target level is empty, it obtains a memory block from the parent free list and splits it to the target level. If the parent free list is empty, it batch-splitting memory blocks from unallocated space includes: When a worker thread initiates a memory allocation request, it reads the thread-local allocation area from the thread-local storage variable, obtains the requested size, performs a bitwise AND operation with the preset bitmask, performs a shift operation on the result to obtain the target level, and accesses the free list corresponding to the target level. If the target level free list is empty, traverse the parent free list, find the first non-empty parent free list, extract a memory block from the first non-empty parent free list as the memory block to be split, calculate the difference between the size of the memory block to be split and the size corresponding to the target level, determine the number of splits based on the difference, perform the split operation corresponding to the number of splits on the memory block to be split, return the target size memory block obtained after splitting to the worker thread, and insert the intermediate size memory block generated during the splitting process into the free list of the corresponding level. If all parent free lists are empty, then a contiguous memory segment is allocated from the unallocated space of the thread's local allocation area. The contiguous memory segment is then divided into multiple memory blocks of the target size according to the size corresponding to the target level. The first memory block of the multiple memory blocks of the target size is returned to the worker thread, and the remaining memory blocks of the target size are inserted into the free list of the target level in sequence.
4. The method according to claim 3, characterized in that, Read the thread-local allocation area from the thread-local storage variable, obtain the requested size, perform a bitwise AND operation with the preset bitmask, perform a shift operation on the result to obtain the target level, and access the free list corresponding to the target level, including: Read the starting address of the thread-local allocation area from the thread-local storage variable of the worker thread, access the metadata area of the thread-local allocation area according to the starting address, obtain the level configuration information of the free list with multi-level size classification from the metadata area, calculate the preset bitmask according to the minimum memory block size in the level configuration information, obtain the requested size in the memory allocation request, and perform a bitwise AND operation between the requested size and the preset bitmask to obtain the mask result. Perform a bit-right shift operation on the mask result, setting the shift number to the binary number of bits corresponding to the minimum memory block size to obtain the level index value. Determine the level index value as the target level. Calculate the head address of the free list of the target level based on the starting address of the thread-local allocation area and the target level. Access the free list corresponding to the target level through the head address.
5. The method according to claim 1, characterized in that, When a worker thread releases a memory block, it uses bitwise operations to retrieve the buddy block and calculates the merge parameters. If the buddy block is found, the merged memory block is inserted into the parent free list, and the merging process is recursively performed, including: When a worker thread initiates a memory release request, it obtains the starting address of the memory block to be released, calculates the offset of the memory block to be released relative to the starting address of the thread's local allocation area, performs a bit right shift operation on the offset, sets the shift number to the number of bits corresponding to the size of the memory block to be released, obtains the memory block number, performs a bitwise inversion operation on the least significant bit of the memory block number to obtain the buddy number, calculates the offset of the buddy block based on the buddy number, adds the starting address of the thread's local allocation area to the buddy block offset to obtain the buddy address, and searches for a buddy block whose starting address matches the buddy address in the free list at the target level. If a buddy block is found, it is removed from the free list at the target level. The sequence number of the memory block to be released is bitwise ANDed with the buddy number to obtain the merge sequence number. The merge start address is calculated based on the merge sequence number. The size of the memory block to be released is left-shifted by one bit to obtain the merge size. The merged memory block is constructed using the merge start address and the merge size. The merged memory block is inserted into the free list at the next higher level. The merged memory block is then used as the new memory block to be released and the process is recursively executed until no buddy block is found or the highest level is reached.
6. The method according to claim 1, characterized in that, Aggregating memory blocks in the free list for reclamation based on address contiguity also includes: Traverse the free list of the level to be reclaimed, extract the starting address of each memory block, and use the quicksort algorithm to sort the starting addresses in ascending order to generate an ordered sequence of memory blocks. Starting from the first memory block in the memory block sequence, calculate the end address of the first memory block and determine whether the end address is equal to the start address of the next memory block; If they are equal, the two memory blocks are merged into one aggregated memory block, the size of the aggregated memory block is updated, and the address continuity between the aggregated memory block and subsequent memory blocks is checked. If they are not equal, the current aggregated memory block is added to the aggregation result queue, and a new aggregation process is started from the next memory block until the memory block sequence is traversed, and the maximized aggregated memory block set is obtained.
7. A multi-threaded memory management system integrating huge pages and thread-local allocation areas, used to implement the method of any one of claims 1-6, characterized in that, include: The memory allocation unit is used to allocate a contiguous large page physical memory region with a single page size larger than the standard page size as a global memory pool; The thread mapping unit is used to divide the global memory pool into multiple thread-local allocation areas, and uses a consistent hashing algorithm to map worker threads to the thread-local allocation areas, storing the starting address of the thread-local allocation area in the thread-local storage variable. The linked list construction unit is used to create a multi-level size-classified free linked list in the thread-local allocation area, wherein the free linked list with a level value greater than the target level is determined as the parent free linked list; The memory allocation unit is used to read the thread-local allocation area from the thread-local storage variable when a worker thread requests memory allocation, map the requested size to the target level, and if the free list of the target level is empty, obtain the memory block from the parent free list and split it to the target level. If the parent free list is empty, then the memory block is split into blocks in batches from the unallocated space. The memory release unit is used to retrieve the buddy block and calculate the merge parameters when the worker thread releases the memory block. If the buddy block is found, the merged memory block is inserted into the parent free list and the merge is performed recursively. The memory reclamation unit calculates the percentage of free memory based on a bitmap. If the percentage exceeds a preset free threshold, it selects a level to be reclaimed based on a popularity score, aggregates the free memory blocks of the reclaimed level, and returns them to the global memory pool. This includes: In the metadata area of the thread-local allocation area, a free memory bitmap is maintained. Each bit in the bitmap corresponds to the occupancy status of a memory block of the smallest granularity. Each time memory is allocated or released, the corresponding position in the bitmap is set or cleared. By performing segmented parallel bit counting operations on the bitmap, the number of free bits is counted, and the percentage of free memory is calculated based on the number of free bits and the total number of bits in the bitmap. Maintain an access timestamp queue for each level of the free list. Each time access occurs, the current timestamp is enqueued. Calculate the access density of each level within a preset time window. Weight the access density with the memory block size to obtain a popularity score. Determine if the percentage of free memory exceeds the preset free threshold. If it does, select the level with the lowest popularity score as the level to be reclaimed. Memory blocks in the free list to be reclaimed are aggregated according to address contiguity. Multiple memory blocks with contiguity are merged into an aggregated memory block, and the target level of the aggregated memory block in the global memory pool is calculated. The aggregated memory blocks are pushed in batches to the recycling buffer of the global memory pool. Based on the background thread of the global memory pool, the insertion operation is completed asynchronously, and the bit state of the corresponding area in the bitmap is updated.
8. An electronic device, characterized in that, include: processor; Memory used to store processor-executable instructions; The processor is configured to invoke instructions stored in the memory to execute the method according to any one of claims 1 to 6.
9. A computer-readable storage medium having computer program instructions stored thereon, characterized in that, When the computer program instructions are executed by the processor, they implement the method described in any one of claims 1 to 6.