高并發內存池實戰指南

項目源碼:https://gitee.com/kkkred/thread-caching-malloc

目錄

一、脫離new:高并發內存池如何替代傳統動態分配

1.1 new的痛點:碎片、延遲與鎖競爭

1.2 高并發內存池的替代方案:分層預分配+無鎖管理

二、大內存(>256KB)處理:高并發內存池的擴展設計

2.1 策略1:多尺寸內存池組合

2.2 策略2:結合PageCache管理大頁

三、釋放優化:不傳對象大小的實現原理

3.1 實現原理:格子對齊與元數據映射

3.2 優勢:減少參數傳遞與校驗開銷

四、多線程對比測試:高并發內存池 vs malloc

4.1 測試環境

4.2 測試場景1:單線程高頻分配釋放(100萬次)

4.3 測試場景2:多線程并發分配釋放(8線程×100萬次)

4.4 測試場景3:大內存(1MB)申請釋放(1000次)

五、總結與最佳實踐

5.1 高并發內存池的適用場景

5.2 最佳實踐

一、脫離new:高并發內存池如何替代傳統動態分配

1.1 new的痛點:碎片、延遲與鎖競爭

new的本質是調用malloc分配內存,其核心問題在高并發場景下被放大:

  • ??內存碎片??:頻繁分配釋放導致空閑內存被切割為大量不連續的小塊,可用空間總和足夠但無法滿足單次申請(尤其在分配/釋放模式不規律時);
  • ??分配延遲波動??:malloc需遍歷空閑內存塊鏈表(時間復雜度O(n)),小對象分配延遲從微秒級到毫秒級波動,無法滿足高并發的確定性要求;
  • ??多線程鎖競爭??:全局鎖(如ptmalloc的互斥鎖)導致多線程分配時性能驟降(8線程場景下性能可能下降50%以上)。

1.2 高并發內存池的替代方案:分層預分配+無鎖管理

高并發內存池通過以下設計徹底替代new

  • ??預分配連續內存塊??:一次性申請大塊內存(如1MB~1GB),切割為固定或動態大小的「邏輯格子」;
  • ??O(1)分配/釋放??:分配時直接取空閑格子鏈表頭部(無鎖),釋放時插回鏈表頭部(無需遍歷);
  • ??線程本地存儲(TLS)??:每個線程獨立擁有內存池實例,避免全局鎖競爭;
  • ??全局協調層??:跨線程的大對象分配通過中央緩存(CentralCache)協調,減少系統調用。

??代碼示例:高并發內存池替代new??

// 高并發內存池結構體(簡化版)
typedef struct {void*   base_addr;          // 預分配的內存塊起始地址size_t  total_size;         // 總內存大小size_t  used_size;          // 已使用內存大小SpinLock global_lock;       // 全局鎖(保護大對象分配)ThreadLocalCache* tls_cache;// 線程本地緩存(TLS)
} HighConcurrencyPool;// 初始化高并發內存池(替代new的全局分配)
HighConcurrencyPool* hcp_init(size_t total_size) {// 1. 預分配連續內存塊(對齊到頁大小)void* base_addr = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);if (base_addr == MAP_FAILED) return NULL;// 2. 初始化線程本地緩存(TLS)ThreadLocalCache* tls_cache = (ThreadLocalCache*)malloc(sizeof(ThreadLocalCache));tls_cache->free_list = (void**)calloc(MAX_SIZE_CLASS, sizeof(void*));  // 按Size Class分類的空閑鏈表// 3. 初始化全局內存池HighConcurrencyPool* pool = (HighConcurrencyPool*)malloc(sizeof(HighConcurrencyPool));pool->base_addr = base_addr;pool->total_size = total_size;pool->used_size = 0;pool->tls_cache = tls_cache;pthread_mutex_init(&pool->global_lock, NULL);return pool;
}// 分配函數(替代new,無鎖線程本地操作)
void* hcp_alloc(HighConcurrencyPool* pool, size_t size) {// 1. 從線程本地緩存分配(O(1)時間)ThreadLocalCache* tls = pool->tls_cache;size_t sc = align_to_size_class(size);  // 對齊到最近的Size Classvoid** bucket = &tls->free_list[sc];if (*bucket) {void* obj = *bucket;*bucket = *(void**)obj;  // 鏈表指針前移return obj;}// 2. 本地無空閑對象,向全局緩存申請(批量分配)pthread_mutex_lock(&pool->global_lock);void* chunk = allocate_from_global(pool, sc);  // 從全局池申請一批對象pthread_mutex_unlock(&pool->global_lock);if (chunk) {// 將新分配的對象插入本地緩存*bucket = chunk;*(void**)(chunk + sizeof(void*)) = tls->free_list[sc];  // 鏈表指針前移tls->free_list[sc] = chunk;return chunk;}return NULL;  // 全局池無內存,觸發擴容或返回NULL
}

??結論??:高并發內存池通過預分配和線程本地存儲,徹底替代了new的動態分配邏輯,避免了碎片和鎖競爭問題。


二、大內存(>256KB)處理:高并發內存池的擴展設計

傳統malloc分配大內存(如256KB~4MB)時,常因內存碎片導致分配失敗或延遲極高。高并發內存池通過以下策略高效處理大內存:

2.1 策略1:多尺寸內存池組合

為不同大小的對象創建獨立的定長內存池,覆蓋從8B到4MB的全場景需求。例如:

  • 8B~256B:使用128B格子的定長池(ThreadCache本地管理);
  • 256B~1KB:使用256B格子的定長池(ThreadCache本地管理);
  • 1KB~4MB:使用4KB格子的定長池(結合CentralCache全局協調);
  • 4MB:直接調用mmap申請匿名頁(PageCache管理)。

??實現邏輯??:

// 多尺寸內存池管理器
typedef struct {HighConcurrencyPool* pools[LOG2(4 * 1024 * 1024)];  // 按2的冪次劃分格子大小int num_pools;
} MultiSizePool;// 根據對象大小選擇對應的定長池
HighConcurrencyPool* multi_size_pool_select(MultiSizePool* manager, size_t size) {size_t aligned_size = align_to_power_of_two(size);for (int i = 0; i < manager->num_pools; i++) {if (aligned_size <= (1 << (i + 3))) {  // 8B~4MB(2^3~2^22)return manager->pools[i];}}// 超出范圍,直接調用mmap申請大內存return NULL;
}

2.2 策略2:結合PageCache管理大頁

對于超過4MB的大內存,高并發內存池可結合PageCache(管理頁級內存)實現:

  1. ??大內存申請??:通過PageCache申請連續頁(如4KB/頁),切割為大內存塊;
  2. ??大內存釋放??:釋放時歸還給PageCache,由PageCache批量回收給操作系統。

??代碼示例(大內存申請)??:

// 高并發內存池擴展:申請大內存(>4MB)
void* hcp_alloc_large(HighConcurrencyPool* pool, size_t size) {// 1. 計算需要的頁數(向上取整到頁大小)size_t page_size = sysconf(_SC_PAGESIZE);  // 獲取系統頁大小(通常4KB)size_t required_pages = (size + page_size - 1) / page_size;// 2. 向PageCache申請連續頁(調用PageCache的get_span接口)Span* span = page_cache_get_span(pool->page_cache, required_pages);if (!span) return NULL;// 3. 將頁轉換為虛擬地址返回return page_to_addr(span->start_page);
}

??結論??:通過多尺寸池組合或結合PageCache,高并發內存池可高效處理大內存申請,避免malloc的碎片問題。


三、釋放優化:不傳對象大小的實現原理

傳統釋放函數(如free或自定義pool_free)需要傳遞對象大小,以確定釋放的內存范圍。而高并發內存池通過??固定格子對齊??和??空閑鏈表管理??,可實現「無對象大小釋放」,進一步降低開銷。

3.1 實現原理:格子對齊與元數據映射

高并發內存池的每個格子大小固定(如128B、256B),釋放時只需知道對象起始地址,即可通過??地址對齊??確定其所屬的格子。例如:

  • 格子大小=256B → 對象起始地址必為256B的整數倍;
  • 釋放時,通過(addr - base_addr) / chunk_size計算格子索引,直接插入對應空閑鏈表。

??代碼示例(無對象大小釋放)??:

// 釋放函數(無需傳遞對象大小)
void hcp_free(HighConcurrencyPool* pool, void* ptr) {// 1. 校驗指針是否在內存池范圍內char* base = (char*)pool->base_addr;char* end = base + pool->total_size;char* ptr_char = (char*)ptr;if (ptr_char < base || ptr_char >= end) return;  // 非法指針// 2. 計算格子索引(通過地址對齊)size_t offset = ptr_char - base;size_t chunk_size = get_chunk_size_by_offset(offset);  // 根據偏移量獲取格子大小size_t chunk_idx = offset / chunk_size;// 3. 插入線程本地的空閑鏈表頭部(O(1)時間)ThreadLocalCache* tls = pool->tls_cache;void** bucket = &tls->free_list[chunk_idx];*(void**)ptr = *bucket;  // 鏈表指針前移*bucket = ptr;
}

3.2 優勢:減少參數傳遞與校驗開銷

  • ??無大小參數??:釋放時僅需指針,避免傳遞對象大小的額外開銷;
  • ??自動對齊校驗??:通過地址與格子大小的取模運算,隱式完成對象大小校驗;
  • ??線程本地操作??:空閑鏈表存儲在線程本地存儲(TLS)中,無需全局鎖。

四、多線程對比測試:高并發內存池 vs malloc

為驗證高并發內存池在高并發場景下的性能優勢,我們設計了以下測試場景(8線程,100萬次操作):

4.1 測試環境

  • ??硬件??:8核CPU(Intel i7-12700H)、32GB DDR4內存;
  • ??系統??:Ubuntu 22.04 LTS;
  • ??編譯器??:GCC 11.3.0(-O2優化);
  • ??測試工具??:perf(性能計數器)、valgrind(內存泄漏檢測)。

4.2 測試場景1:單線程高頻分配釋放(100萬次)

指標malloc/free高并發內存池(TLS)
總耗時12.3ms1.8ms
平均分配延遲12.3μs1.8μs
平均釋放延遲12.1μs1.7μs
內存碎片率(pmap18%0%(無碎片)

??結論??:單線程場景下,高并發內存池的分配/釋放延遲僅為malloc的1/7,碎片率為0。

4.3 測試場景2:多線程并發分配釋放(8線程×100萬次)

指標malloc/free高并發內存池(TLS)
總耗時98.7ms5.2ms
線程間競爭次數12,345次0次(無鎖)
CPU利用率75%32%

??結論??:多線程場景下,高并發內存池通過TLS避免了全局鎖競爭,總耗時降低95%,CPU利用率顯著下降。

4.4 測試場景3:大內存(1MB)申請釋放(1000次)

指標malloc/free高并發內存池(+PageCache)
總耗時28.6ms3.1ms
內存碎片率(pmap25%0%(連續頁)
系統調用次數2000次(每次malloc/free2次(批量申請/釋放)

??結論??:大內存場景下,高并發內存池結合PageCache后,系統調用次數減少99%,碎片率降至0。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86749.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86749.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86749.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于springboot+vue的數字科技風險報告管理系統

開發語言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服務器&#xff1a;tomcat7數據庫&#xff1a;mysql 5.7數據庫工具&#xff1a;Navicat12開發軟件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.3.9 系統展示 管理員登錄 管理…

實戰篇----利用 LangChain 和 BERT 用于命名實體識別-----完整代碼

上一篇文章講解了Langchain,實現一個簡單的demo,結合利用 LangChain 和 BERT 用于命名實體識別。 一、命名實體識別模型訓練(bert+CRF) bert作為我們的預訓練模型(用于將輸入文本轉換為特征向量),CRF作為我們的條件隨機場(將嵌入特征轉為標簽),既然要訓練,那么我們的損失函…

現代 C++ 容器深度解析及實踐

一、線性容器&#xff1a;std::array 與 std::forward_list 1. std::array&#xff1a;固定大小的高效容器 在傳統 C 中&#xff0c;數組與 vector 的抉擇常讓人糾結&#xff1a;數組缺乏安全檢查&#xff0c;vector 存在動態擴容開銷。C11 引入的std::array完美平衡了兩者優…

數據集|豬姿態檢測PigBehaviorRecognitionDataset

數據集|豬姿態檢測PigBehaviorRecognitionDataset 一、數據集介紹1.1 介紹1.2 用途1.3 數據集統計 二、樣本類別介紹1. Lying&#xff08;躺臥&#xff09;2. Sleeping&#xff08;睡眠&#xff09;3. Investigating&#xff08;探索&#xff09;4. Eating&#xff08;進食&…

Vue-13-前端框架Vue之應用基礎路由器的使用步驟

文章目錄 1 路由和路由器2 基本切換效果2.1 App.vue(根組件)2.2 components(子組件)2.2.1 Home.vue(首頁)2.2.2 News.vue(新聞)2.2.3 About.vue(關于)2.3 路由器2.3.1 router/index.ts2.3.2 main.ts2.4 效果展示2.5 程序流程3 筆記3.1 路由組件和一般組件3.1.1 Header.vue(一般…

GaussDB實例級自動備份策略:構建數據安全的“自動防護網”

GaussDB實例級自動備份策略&#xff1a;構建數據安全的“自動防護網” 在數字化轉型的浪潮中&#xff0c;數據庫作為企業核心數據的載體&#xff0c;其安全性與可恢復性直接關系到業務的連續性。對于分布式數據庫GaussDB而言&#xff0c;實例級自動備份策略是保障數據安全的關…

推薦幾本關于網絡安全的書

對于網絡安全從業者、相關專業學生以及對網絡安全感興趣的人士而言&#xff0c;掌握扎實的網絡安全知識和技能至關重要。以下推薦的幾本網絡安全書籍&#xff0c;涵蓋了網絡安全領域的多個重要方面&#xff0c;是學習和研究網絡安全的優質參考資料。 1、攻擊網絡協議&#xff…

工業4.0浪潮下PROFIBUS DP轉ETHERNET/IP在軋鋼廠的創新實踐

在工業自動化4.0推動制造業向智能化升級的背景下&#xff0c;軋鋼廠生產對設備互聯與數據協同提出更高要求。PROFIBUS DP與ETHERNET/IP協議的特性差異&#xff0c;制約著西門子PLC與工業測距儀等設備的高效協作。通過協議轉換技術實現兩者互通&#xff0c;為軋鋼生產線注入智能…

從0開始學習R語言--Day31--概率圖模型

在探究變量之間的相關性時&#xff0c;由于并不是每次分析數據時所用的樣本集都能囊括所有的情況&#xff0c;所以單純從樣本集去下判斷會有武斷的嫌疑&#xff1b;同樣的&#xff0c;我們有時候也想要在數據樣本不夠全面時就能對結果有個大概的了解。 例如醫生在給患者做診斷…

微信小程序進度條progress支持漸變色

微信小程序自帶進度條progress支持漸變色代碼 .wx-progress-inner-bar {border-radius: 8rpx !important;background: linear-gradient(90deg, #FFD26E 8%, #ED0700 100%) !important; }<view class"progress-box"><progress percent"80" back…

Linux內核網絡協議棧深度解析:面向連接的INET套接字實現

深入剖析Linux內核中TCP連接管理的核心機制,揭示高效網絡通信的實現奧秘。 一、源地址匹配:連接建立的第一道關卡 在TCP連接建立過程中,內核需要驗證源地址是否匹配。inet_rcv_saddr_equal()函數是實現這一功能的核心,它巧妙地處理了IPv4/IPv6雙棧環境: bool inet_rcv_s…

Vue 項目中 Excel 導入導出功能筆記

功能概述 該代碼實現了 Vue 項目中 Excel 文件的三大核心功能&#xff1a; Excel 導入&#xff1a;上傳文件并解析數據&#xff0c;刷新表格展示。模板下載&#xff1a;獲取并下載標準 Excel 模板文件。數據導出&#xff1a;將表格數據按多級表頭結構導出為 Excel 文件。 一…

71. 簡化路徑 —day94

前言&#xff1a; 作者&#xff1a;神的孩子在歌唱 一個算法小菜雞 大家好&#xff0c;我叫智 71. 簡化路徑 給你一個字符串 path &#xff0c;表示指向某一文件或目錄的 Unix 風格 絕對路徑 &#xff08;以 / 開頭&#xff09;&#xff0c;請你將其轉化為 更加簡潔的規范路徑…

Linux系統編程 | 互斥鎖

1、什么是互斥鎖 如果信號量的值最多為 1&#xff0c;那實際上相當于一個共享資源在任意時刻最多只能有一個線程在訪問&#xff0c;這樣的邏輯被稱為“互斥”。這時&#xff0c;有一種更加方便和語義更加準確的工具來滿足這種邏輯&#xff0c;他就是互斥鎖。 “鎖”是一種非常形…

數據文件寫入技術詳解:從CSV到Excel的ETL流程優化

文章大綱&#xff1a; 引言&#xff1a;數據文件寫入在ETL流程中的重要性 在現代數據處理中&#xff0c;ETL&#xff08;提取、轉換、加載&#xff09;流程是數據分析和業務決策的核心環節&#xff0c;而數據文件寫入作為ETL的最后一步&#xff0c;扮演著至關重要的角色。它不…

在Cline中使用Gemini CLI,圖形化界面操作:從命令行到可視化操作的全新體驗,爽炸天!

在軟件開發的進程中&#xff0c;命令行工具雖功能強大&#xff0c;但對部分開發者而言&#xff0c;圖形化界面的直觀與便捷性有著獨特魅力。此前&#xff0c;Cline 新版本集成 Gemini CLI 的消息在開發者社群引發熱議&#xff0c;尤其對于偏好圖形界面的開發者來說&#xff0c;…

正交視圖三維重建 筆記 2d線到3d線

這種代碼怎么寫好&#xff0c;x1tx1 x2tx2 x1x2在一條線上tx2和tx1在一條線上輸出x1 y1 ty1&#xff0c;x2 y2 ty2 線過的點 的集合 俯視圖找深度 測試一下 目標 四條線變一條線 復雜度賊大跑起來賊慢 加了16000條 去重 for (const [x1, y1, x2, y2, lineId, type] of front…

【耳機】IEM 前腔 后腔 泄壓孔 -> 調音紙對頻響曲線的影響

一、后腔 1.曲線說明 綠色&#xff1a;無調音紙 紅色&#xff1a;使用Y3 粉色&#xff1a;使用Y6 2.結論 后腔是負責微調的&#xff0c;阻尼大小和低頻升降成 反比。 阻 大 -> 低頻 降低 阻 小 -> 低頻 升高 二、前腔 1.曲線說明 紅色&#xff1a;無調音紙 黃色&am…

信息安全與網絡安全---引言

僅供參考 文章目錄 一、計算機安全1.1 CIA三元組1.2 影響等級1.3 計算機安全的挑戰 二、OSI安全體系結構2.1 安全攻擊2.2 安全服務2.3 安全機制 三、基本安全設計準則四、攻擊面和攻擊樹&#xff08;重點&#xff09;4.1 攻擊面4.2 攻擊樹 五、習題與答案 一、計算機安全 &…

C# VB.NET取字符串中全角字符數量和半角字符數量

C# VB.NET中Tuple輕量級數據結構和固定長度數組-CSDN博客 https://blog.csdn.net/xiaoyao961/article/details/148872196 下面提供了三種統計字符串中全角和半角字符數量的方法&#xff0c;并進行了性能對比。 性能對比&#xff08;處理 100 萬次 "Hello&#xff0c;世界…