內存分配器ptmalloc2、tcmalloc、jemalloc,結構設計、內存分配過程詳解

1. 引言

博主之前做過一個高并發內存池的項目實踐,在實踐中對于內存分配器的內存分配過程理解更加深刻了。在此期間,翻查了不少資料以及博客,發現源碼分享的博客不多,能生動完整的講述ptmalloc2、tcmalloc、jemalloc它們的結構設計以及內存分配過程的博客更是少之又少。那么在這篇文章中博主將總結分享內存分配器ptmalloc2、tcmalloc、jemalloc,歡迎大家留言討論!

2. 現狀

目前大部分服務端程序使用glibc提供的 malloc/free 系列函數,而glibc使用的 ptmalloc2 在性能上遠遠弱后于googletcmallocfacebookjemalloc。 而且后兩者只需要使用LD_PRELOAD環境變量啟動程序即可,甚至并不需要重新編譯。

3. 業務場景

分配內存時進行系統調用的接口,對 heap 的操作, 操作系統提供了 brk() 系統調用,設置了Heap的上邊界; 對 mmap 映射區域的操作,操作系統供了 mmap()munmap() 函數。

因為系統調用的代價很高,不可能每次申請內存都從內核分配空間,尤其是對于小內存分配。 而且因為 mmap 的區域容易被 munmap 釋放,所以一般大內存采用 mmap(),小內存使用 brk()。

4. glibc ptmalloc2

最新版本:作為Glibc的默認內存分配器,ptmalloc2的更新與Glibc(目前更新到2.35+)版本綁定。截至當前,其核心設計仍基于最初的ptmalloc2架構,?glibc 2.26(2017年)開始引入了線程本地緩存(tcache,Thread Local Caching)。

4.1 內存管理結構

線程本地緩存(tcache)
  • 作用:無鎖快速分配小內存(每個線程獨立)。

  • 管理范圍:默認 ≤ 1032 字節(64 位系統)。

  • 數據結構

    • 64 個?tcache_bin(單鏈表,LIFO),每個 bin 對應一種固定大小(8B~1032B)。

    • 每個 bin 最多緩存 7 個 chunk(可通過?GLIBC_TUNABLES?調整)。

  • 優化點

    • 優先從?tcache?分配/釋放,完全無鎖。

    • 若?tcache?為空,則從?fastbins/smallbins?批量預加載(加鎖)。

Fast Bins(快速通道)
  • 作用:緩存最近釋放的小內存(仍保留 chunk 未合并,加速復用)。

  • 管理范圍:≤ 160 字節(64 位系統)。

  • 數據結構:7 個單鏈表(LIFO),每個鏈表存儲相同大小的 chunk。

  • 與 tcache 的關系

    • tcache?不足時從?fastbins?補充。

    • free()?時,小 chunk 可能先進入?tcache,若?tcache?滿則進入?fastbins

Small Bins(小內存通道)
  • 作用:管理中等大小的內存(固定大小,合并友好)。

  • 管理范圍:≤ 1008 字節(64 位系統)。

  • 數據結構:62 個雙向鏈表(FIFO),每個 bin 對應一種固定大小(如 16B, 24B, ..., 1008B)。

  • 與 tcache 的關系

    • tcache?不足時從?smallbins?分配。

    • free()?時,若 chunk 大小匹配且?tcache?滿,則進入?smallbins

Large Bins(大內存通道)
  • 作用:管理大內存塊(范圍區間,按大小排序)。

  • 管理范圍:> 1008 字節。

  • 數據結構:63 個雙向鏈表,每個 bin 管理一個大小范圍(如 1024B~1088B, 1089B~1152B...)。

  • 分配策略

    • 查找最小滿足大小的 chunk(可能分割)。

    • 若找不到,則進入?unsorted bin?或?top chunk

Unsorted Bin(臨時緩沖區)
  • 作用:臨時存放釋放的 chunk,等待重新分類。

  • 數據結構:1 個雙向鏈表(FIFO),存放最近釋放的 chunk(不分大小)。

  • 分配流程

    • 分配時優先檢查遍歷?unsorted bin,若找到合適 chunk 直接返回,否則將其轉移到?small/large bins

Top Chunk(堆頂塊)
  • 作用:當前堆的頂部未分配內存,用于動態擴展。

  • 分配邏輯

    • 當所有 bins 無法滿足請求時,從 Top Chunk 切割所需內存。

    • 若 Top Chunk 不足,通過?sbrk(主分配區)或?mmap(非主分配區)擴展堆空間。

mmap 通道(超大內存)
  • 管理大小:默認 ≥ 128KB(可通過?M_MMAP_THRESHOLD?調整)。

  • 特點

    • 直接通過?mmap?分配獨立內存段,繞過堆管理。

    • 釋放時通過?munmap?立即歸還系統,避免碎片。

last remainder chunk(最后剩余塊)
  • 優化連續內存分配:當用戶請求的內存略小于某個空閑塊時,分割后剩余的碎片會被標記為?last remainder,優先用于后續的連續分配請求。

  • 減少外部碎片,提升內存利用率。

4.2 內存分配流程

4.3 多線程支持

  • 主分配區(Main Arena):主線程默認使用,通過?brk?擴展堆。

  • 非主分配區(Thread Arena):其他線程按需創建(數量受?MALLOC_ARENA_MAX?限制)。

  • 鎖優化

    • 每個分配區有獨立的鎖,線程優先訪問綁定的分配區。

    • 若所有分配區被占用,線程會競爭主分配區。

4.4 缺點

  • 內存碎片:外部和內部碎片問題突出,尤其長期運行后。
  • 鎖競爭:盡管有 tcache 和分配區,高并發下仍可能成為瓶頸。
  • 釋放延遲:fastbins 延遲合并,brk?堆收縮不積極。
  • 場景局限:不適合高頻小對象分配或實時系統。

5. tcmalloc

最新版本:截至2025年,tcmalloc已迭代至支持per-CPU模式的優化版本(如v2.10+),進一步提升多核性能。場景適用于多核服務器(如 Web 服務、數據庫),高頻小對象分配(如微秒級響應的 RPC 框架)。

5.1 內存管理結構

CPU Local Cache(CPU 本地緩存)

  • 申請對象為小內存(通常 ≤256KB)。

  • 使用Size Class 哈希桶,每個 CPU 核心維護一組自由鏈表(FreeList),按大小分類(如 8B、16B、...、256KB)。

  • 動態批量填充,當本地緩存不足時,從下層批量獲取對象(類似慢啟動算法)。

  • 這里無鎖

Central Free List(中心空閑列表)
  • 這一層全局共享,作為 CPU 本地緩存的后備倉庫。

  • 結構為哈希桶Span 鏈表:按大小分類管理內存塊(Span),每個 Span 被切分為多個小對象。

  • 輕量級鎖:僅當 CPU 緩存需要補充時加鎖(鎖粒度細化到 Size Class)。

  • 申請內存,當CPU 緩存耗盡時,從 Central Free List 批量拉取對象(如一次獲取 32 個 16B 的塊)。Central Free List 自身從 PageHeap 申請新的 Span。

PageHeap(頁堆)

  • 以頁(通常 8KB)為粒度的大內存。

  • 合并相鄰空閑頁,減少外碎片。

  • 基數樹(Radix Tree)算法,快速映射內存頁到 Span,支持 O(1) 查找。
  • 超過 256KB 的請求直接由 PageHeap 處理(通過?mmap?或?VirtualAlloc)。

5.2 內存分配流程(Per-CPU 模式)

  1. 小內存分配(≤256KB)時,線程先獲取當前 CPU 的本地緩存(GetThisCPUCache()),根據請求大小找到對應的 Size Class 自由鏈表。若鏈表非空,直接彈出對象返回(無鎖),若鏈表為空,從 Central Free List 批量獲取對象(加鎖,但頻率低)。
  2. 小內存釋放時,將對象放回當前 CPU 的本地緩存自由鏈表(無鎖)。若本地緩存超過閾值(如 1024 個對象),觸發批量歸還到 Central Free List。

  3. 大內存分配(>256KB)時,直接由 PageHeap 分配,通過?mmap?或?VirtualAlloc?申請大塊內存。
  4. 大內存釋放時通過 PageHeap 的基數樹找到對應 Span,標記為空閑。嘗試合并相鄰空閑 Span,形成更大的連續內存。

5.4 局限性

它是零鎖競爭的,Per-CPU 緩存完全無鎖,適合超高并發場景。本地化操作減少 CPU 緩存行失效(Cache Line Ping-Pong),所以它延遲低。支持彈性擴展,CPU 數量增加時,性能線性提升(無全局瓶頸)。

Per-CPU 緩存可能暫存未使用的對象(可通過調優閾值緩解),造成內存浪費。它對CPU比較依賴,若線程頻繁遷移 CPU,性能會下降(需綁定線程到核心)。

6. jemalloc

最新版本jemalloc 5.3.0(發布于2025年5月1日),進一步優化內存碎片管理和多線程擴展性。

6.1 內存管理結構

之前的版本

在之前的版本中內存被劃分成若干個arena;每個arena被劃分成若干個chunk(每個默認為4M);每個chunk被劃分成若干個run;每個run由若干個page組成(每個page默認為4K),同一個run中的page又被細分成若干個大小相同的region;

為了減少內存碎片及快速定位到合適大小的內存,jemalloc將run按以下class_size分成44類(同一個run下的region大小相同):

  • small(<4K): [8], [16, 32, 48, …, 128], [192, 256, 320, …, 512], [768, 1024, 1280, …, 3840]

  • large(4K-4M): [4K, 8K, 12K, …, 4072K]

  • huge(>4M): [4M, 8M, 12M, …]

  • 對于小于4K內存的申請,jemalloc直接向上取整到最小的class_size,例如申請1-7字節,都會分配一個8字節內存。

新版本

在?jemalloc 5.3.0?中,舊版的?chunkrun?等概念被?Extent?統一取代,其核心設計如下:

1. Extent(擴展塊)
  • 表示連續的虛擬內存區域(大小靈活,通常為 2MB 或 4MB,可動態調整)。替代舊版的?chunk,支持更細粒度的拆分與合并。

  • 狀態分級:分為?Active(在用)、Dirty(已釋放但未擦除)、Muzzy(部分擦除)、Retained(完全擦除保留)。

  • 動態管理:支持按需分割為?Slab?或直接分配。

2. Slab(內存板)
  • 將?Extent?劃分為固定大小的槽位(如 32B、64B),通過位圖管理分配狀態。替代舊版?run?的功能,但更輕量化。

  • 按需分配:僅在使用時初始化?Slab,減少內存占用。

  • 延遲釋放:空閑?Slab?不會立即合并,保留在?Dirty/Muzzy?狀態供快速復用。

3. Page(頁)
  • 保留它的意義,仍作為操作系統內存管理的最小單位(如 4KB),但 jemalloc 內部通過?Extent?聚合多頁。

  • 用戶無需關注頁級操作,所有分配均通過?Extent?和?Slab?抽象。

4. Region(區域)
  • 舊版中模糊的?region?概念被?Extent?和?Slab?明確取代,不再存在于官方文檔。

6.2 內存分配流程

1. 從線程緩存(Thread Cache)分配
  • 當請求的內存大小?≤ 14KB(小對象)。線程本地緩存(TLS)中對應的?Size Class 有空閑槽位。根據請求大小匹配對應的?Size Class(如 32B、64B),從線程本地的?Slab 空閑鏈表?中彈出第一個空閑槽位,直接返回槽位內存地址(無鎖操作)。

  • 這個過程是零鎖競爭,速度極快(納秒級)。適合高頻小對象分配(如短生命周期對象)。

2. 從 Arena 分配
  • 當線程緩存為空,且請求大小?≤ 4MB(中等對象)。或請求大小?>14KB 但 ≤4MB(跳過線程緩存)。

  • 線程綁定到特定 Arena(默認輪詢或哈希分配)。若當前 Arena 鎖競爭激烈,可能創建新 Arena。從 Arena 的 Extent/Slab 分配,如果是中等對象(14KB < size ≤4MB),直接分配整個?Extent?或從空閑 Extent 鏈表分割。如果是小對象(≤14KB):從 Arena 的?Slab?中批量獲取多個槽位,填充線程緩存后返回一個。

  • 鎖機制:Arena 內部使用細粒度鎖(如每個 Extent 獨立鎖),減少競爭。

3. 從系統內存(mmap/brk)分配
  • 當我們請求大小?>4MB(大對象)或 Arena 的 Extent 不足(需擴展堆空間)時。直接調用 mmap,記錄元信息到全局基數樹(Radix Tree)。通過?mmap?申請獨立內存段(默認 ≥4MB)。這時是大對象,不進入線程緩存或 Arena,直接由全局結構跟蹤。釋放時調用?munmap?立即歸還系統,避免碎片。

  • 可以避免污染線程緩存和 Arena。減少大內存的合并開銷。

6.3 內存回收機制

1. 線程本地緩存(Thread Cache)的回收

  • 用戶釋放的小對象(≤4KB)首先存入線程本地緩存(Thread Cache)的對應?size class?槽位。

  • 批量回收每個?size class?的 Thread Cache 槽位有最大保留數量(如默認 200 個)。當超出閾值時,jemalloc 將多余的槽位批量歸還到全局的?Arena

2. 全局分配區(Arena)的回收

  • 每個 Arena 維護一組?extent(如 4MB 的大塊內存),按?size class?分類。Extent 分割為?slab?后,jemalloc 跟蹤每個 slab 中已分配和空閑的槽位(Slot)。

  • Slab 完全空閑時,即當某個 slab 的所有槽位均被釋放時,其所屬的 Extent 可能被標記為空閑。jemalloc 定期內存壓力檢測或根據系統內存壓力,合并空閑 Extent 并歸還操作系統。

3. Extent 的合并與歸還

  • 惰性合并(Lazy Coalescing)jemalloc 不會立即合并相鄰的空閑 Extent,而是保留它們以便快速響應未來可能的相同大小請求。當連續的空閑 Extent 達到一定數量或系統內存不足時,主動合并以減少外部碎片。

  • 操作系統歸還完全空閑的 Extent 可能通過?munmap?歸還操作系統。jemalloc 默認保留部分空閑 Extent(通過?retain:true?配置),避免頻繁的?mmap/munmap?系統調用開銷。

6.4 局限性

  • 內存碎片積累(尤其多 Arena 場景)。
  • 線程本地緩存囤積?導致內存利用率下降。
  • 超大對象管理開銷?和?配置復雜度高
  • 平臺兼容性差異?和?調試困難

參考資料:

內存優化總結:ptmalloc、tcmalloc和jemalloc_ptmalloc jemalloc tcmalloc-CSDN博客

ptmalloc VS tcmalloc VS jemalloc 原理及對比測試

ptmalloc代碼淺析2(small bin/large bin結構圖)_largebin-CSDN博客


本篇文章對內存分配器ptmalloc2、tcmalloc、jemalloc,結構設計、內存分配過程做了詳解!歡迎留言討論!

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

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

相關文章

【擁抱AI】Deer-Flow字節跳動開源的多智能體深度研究框架

最近發現一款可以對標甚至可能超越GPT-Researcher的AI深度研究應用&#xff0c;Deer-Flow&#xff08;Deep Exploration and Efficient Research Flow&#xff09;作為字節跳動近期開源的重量級項目&#xff0c;正以其模塊化、靈活性和人機協同能力引發廣泛關注。該項目基于 La…

openfeign與dubbo調用下載excel實踐

一、前言 openfeign和dubbo均是rpc框架 RPC&#xff08;Remote Procedure Call&#xff0c;遠程過程調用&#xff09;框架 是一種允許程序像調用本地方法一樣調用遠程服務器上函數的技術。它隱藏了底層網絡通信的復雜性&#xff0c;讓開發者可以專注于業務邏輯&#xff0c;實現…

解密企業級大模型智能體Agentic AI 關鍵技術:MCP、A2A、Reasoning LLMs-強化學習算法

解密企業級大模型智能體Agentic AI 關鍵技術&#xff1a;MCP、A2A、Reasoning LLMs-強化學習算法 現在我們的核心問題是有一些同學會知道要才能強化學習。為什么才能強化學習&#xff1f;是實現AGI。例如從這個其實你從第一階段開始以后&#xff0c;就是chatbot&#xff0c;這…

音頻分類的學習

1.深度學習PyTorch入門-語音分類 https://blog.csdn.net/sinat_41787040/article/details/129795496 https://github.com/musikalkemist/pytorchforaudio https://github1s.com/musikalkemist/pytorchforaudio/blob/main/04%20Creating%20a%20custom%20dataset/urbansoundda…

美SEC主席:探索比特幣上市證券交易所

作者/演講者&#xff1a;美SEC主席Paul S. Atkins 編譯&#xff1a;Liam 5月12日&#xff0c;由美國SEC加密貨幣特別工作組發起的主題為《資產上鏈&#xff1a;TradFi與DeFi的交匯點》系列圓桌會議如期舉行。 會議期間&#xff0c;現任美SEC主席Paul S. Atkins發表了主旨演講。…

Qt file文件操作詳解

1.引言 很多應用程序都具備操作文件的能力&#xff0c;包括對文件進行寫入和讀取&#xff0c;創建和刪除文件等等&#xff0c;甚至某些應用程序的就是為了操作文件&#xff0c;像WPS Office。基于此Qt框架中專門提供了對文件操作的類&#xff1a;QFile。 2.QFile文件操作 QF…

【測試開發知識儲備】之Jacoco(Java Code Coverage)

文章目錄 Jacoco是什么Jacoco的主要功能&#xff08;一&#xff09;多樣化覆蓋率指標分析&#xff08;二&#xff09; 豐富的報告生成&#xff08;三&#xff09;實時數據收集 Jacoco的工作原理&#xff08;一&#xff09;字節碼增強&#xff08;二&#xff09;測試執行與數據收…

Docker 介紹與使用

Docker 文章目錄 Docker介紹與虛擬機的比較啟動速度占用資源 優勢更容易遷移更容易維護更容易擴展 使用場景持續集成提供可伸縮的云服務搭建微服務架構 鏡像與容器鏡像構成&#xff08;分層結構&#xff09;鏡像與容器的區別 安裝 Docker常用命令介紹鏡像相關容器相關 實戰&…

《AI大模型應知應會100篇》第62篇:TypeChat——類型安全的大模型編程框架

第62篇&#xff1a;TypeChat——類型安全的大模型編程框架 摘要 在構建 AI 應用時&#xff0c;一個常見的痛點是大語言模型&#xff08;LLM&#xff09;輸出的不確定性與格式不一致問題。開發者往往需要手動解析、校驗和處理模型返回的內容&#xff0c;這不僅增加了開發成本&a…

upload-labs通關筆記-第5關 文件上傳之.ini繞過

目錄 一、ini文件繞過原理 二、源碼審計 三、滲透實戰 1、查看提示 2、制作.user.ini文件 &#xff08;1&#xff09;首先創建一個文本文件 &#xff08;2&#xff09;保存文件名為.user.ini 2、制作jpg后綴腳本 &#xff08;1&#xff09;創建一個文本文件 &#xf…

為什么 Linux 上默認沒有 host.docker.internal

在 Linux 環境中&#xff0c;host.docker.internal 是 Docker 為容器提供的一個特殊 DNS 名稱&#xff0c;用于指向宿主機的 IP 地址&#xff08;類似 macOS/Windows 中的行為&#xff09;。但這個功能在 Linux 上默認不啟用&#xff0c;需要手動配置才能使用。以下是詳細解釋和…

C++GO語言微服務和服務發現②

01 創建go-micro項目-查看生成的 proto文件 02 創建go-micro項目-查看生成的main文件和handler ## 創建 micro 服務 命令&#xff1a;micro new --type srv test66 框架默認自帶服務發現&#xff1a;mdns。 使用consul服務發現&#xff1a; 1. 初始consul服務發現&…

Redis--常見數據類型List列表

目錄 一、概念 二、命令 2.1 LPUSH 2.2 LPUSHX 2.3 RPUSH 2.4 RPUSHX 2.5 LRANGE 2.6 LPOP 2.7 RPOP 2.8 LINDEX 2.9 LINSERT 2.10 LLEN 2.11 阻塞版本命令 三、內部編碼 一、概念 列表類型是用來存儲多個有序的字符串&#xff0c;列表中的每個字符串稱為元素&…

QListWedget控件使用指南

QListWedget公共函數 函數簽名功能描述QListWidget(QWidget *parent nullptr)構造函數&#xff0c;創建一個QListWidget對象&#xff0c;可指定父部件&#xff08;默認為nullptr&#xff09;。virtual ~QListWidget()虛析構函數&#xff0c;釋放QListWidget對象及其資源。voi…

Seata源碼—1.Seata分布式事務的模式簡介

大綱 1.Seata分布式事務框架簡介 2.Seata AT模式實現分布式事務的機制 3.Seata AT模式下的寫隔離機制 4.Seata AT模式下的讀隔離機制 5.官網示例說明Seata AT模式的工作機制 6.Seata TCC模式的介紹以及與AT模式區別 7.Seata Saga模式的介紹 8.單服務多個庫的分布式事務…

【Qt】之音視頻編程2:QtAV的使用篇

QtAV 基本播放控制功能實現&#xff08;C & QML&#xff09; QtAV 提供了完整的播放控制 API&#xff0c;支持 播放、暫停、停止、快進快退、截屏 等功能。以下是具體實現方法&#xff1a; 1. C 控制方式 基本播放控制 #include <QtAV> #include <QtAV/AVPlaye…

歌詞滾動效果

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><!-- 設置標簽頁圖標 --><link rel"shortcut icon&…

基于大模型的TIA診療全流程智能決策系統技術方案

目錄 一、多模態數據融合與預處理系統1.1 數據接入模塊1.2 數據預處理偽代碼二、TIA智能預測模型系統2.1 模型訓練流程2.2 混合模型架構偽代碼三、術中智能監測系統3.1 實時監測流程3.2 實時預測偽代碼四、智能診療決策系統4.1 手術方案推薦流程4.2 麻醉方案生成偽代碼五、預后…

Java 日期解析與格式化:從標準格式到自然語言解析

使用 Java 搭配 Apache Commons Lang3 和 Natty 庫&#xff0c;實現靈活高效的日期解析與格式化。 一、背景 將不同格式的日期統一成一個格式。日期格式可能有以下幾種類型&#xff1a; 標準格式&#xff1a;2024-02-28、14/05/2022、2002年5月6日非英文月份縮寫&#xff1a;…

Room持久化庫:從零到一的全面解析與實戰

簡介 在Android開發中,Room作為官方推薦的數據庫持久化庫,提供了對SQLite的抽象層,使得數據庫操作更加安全、高效且易于維護。 Room通過注解處理器和編譯時驗證,顯著降低了數據庫操作的復雜度,同時支持響應式編程模式,使開發者能夠輕松實現數據變化的實時監聽。對于企業…