記憶體池原理大揭秘

来源:https://www.cnblogs.com/qcloud1001/archive/2018/11/14/9956714.html
-Advertisement-
Play Games

歡迎大家前往 "騰訊雲+社區" ,獲取更多騰訊海量技術實踐乾貨哦~ 本文由 "[amc" ](https://cloud.tencent.com/developer/user/1024461?fromSource=waitui)發表於 "雲+社區專欄" 在 C 語言的動態申請記憶體技術中,相比起 /` ...


歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~

本文由[amc](https://cloud.tencent.com/developer/user/1307425)發表於雲+社區專欄

在 C 語言的動態申請記憶體技術中,相比起 alloc/free 系統調用,記憶體池(memory pool)是與現在系統中請求一大片連續的記憶體空間,然後在運行時根據實際需要分配出去的技術。使用記憶體池的優點有:

  1. 速度遠比 malloc/free 快,因為減少了系統調用的次數,特別是頻繁申請/釋放記憶體塊的情況
  2. 避免了頻繁申請/釋放記憶體之後,系統的大量記憶體碎片
  3. 節省空間

分類

根據分配出去的記憶體大小,記憶體池可以分為兩類:

Fixed-size Allocation

每次分配出去的記憶體單元(稱為 unit 或者 cell)的大小為程式預先定義的值。釋放記憶體塊時,則只需要簡單地掛回記憶體池鏈表中即可。又稱為 “固定尺寸緩衝池”。

常規的做法是:將不同 unit size 的記憶體池整合在一起,以滿足不同記憶體塊大小的使用需求

Variable-size allocation

不分配固定長度,記憶體的分配只是在一大塊空閑的記憶體上滑動。優點是分配效率很高,缺點是成批地回收記憶體,因為釋放的記憶體無法直接重覆利用。

使用這種需要合理規劃每塊記憶體的管理區域,所以又叫做 “基於區域的” 記憶體管理。使用這種做法的分配器,舉例有 Apache Portable Runtime 中的 apr_pool 工具。本文不討論這種記憶體池。


原理和結構

概念和數據結構

定長記憶體池有一些基本和必要的概念,需要定義在記憶體池的結構數據中。以下命名方式使用變體的匈牙利命名法,比如 nNextn表示變數類型為整形。類似地,p表示指針。

Memory Unit

每次程式調用 MemPool_Alloc 獲取一個記憶體區域後,會獲得一塊連續的記憶體區域。管理一個這樣的記憶體區域的單元就成為記憶體單元 unit,有時也稱作 chunk。每個 unit 需要包含以下數據:

  1. nNext:整型數據,表示下一個可供分配的 unit 的標識號。功能請參見後問
  2. pData[]:實際的記憶體區域,其大小在創建時由調用方指定

Memory Block

一個記憶體塊,記憶體塊中保存著一系列的記憶體單元。

這個數據結構需要包含以下基本信息:

  1. nSize:整型數據,表示該 block 在記憶體中的大小
  2. nFree:整型,表示剩下有幾個 unit 未被分配
  3. nFirst:整型,表示下一個可供分配的 unit 的標識號
  4. pNext:指針,指向下一個 memory block

Memory Pool

一個記憶體池總的管理數據結構,換句話說,是一個記憶體池對象。

  1. pBlock:指針,指向第一個 memory block
  2. nUnitSize:整型,表示每個 unit 的尺寸
  3. nInitSize:整型,表示第一個 block 的 unit 個數
  4. nGrowSize:整型,表示在第一個 block 之外再繼續增加的每個 block 的 unit 個數

函數介面

作為一個記憶體池,需要實現以下一些基本的函數介面,或者說可以是對象方法:

memPoolCreate()

創建一個 memory pool,必須的參數為 unit size,可選參數為上文 memory pool 的 nInitSizenGrowSize

memPoolDestroy()

銷毀整個 memory pool 並交還給操作系統。

memPoolAlloc()

從 memory pool 中分配一個 unit,其尺寸是預先定義的 unit size。

memPoolFree()

釋放一個指定的 unit。


工作過程

現在我們用一個 unit size 為 1024、init size 為 4(每一個 block 有 4 個 units)的 memory pool 為例,解釋一下記憶體池的工作原理。下文假設整型的寬度為 4 個位元組。

創建 memory pool

程式開始,調用並創建一個 memory pool。此時調用的函數為 memPoolCreate(),程式會創建一個數據結構,相應的結構體成員及其取值如下:

img

memory pool alloc

當調用者第一次請求 memPoolAlloc() 時,記憶體池發現 block 鏈表為空,於是想系統申請記憶體,創建 memory block,並初始化如下(其中地址值為假設值):

img

其中 nSize = 4112 = sizeof(memPool) + nInitSize * sizeof(memUnit)。每一個 nNext 依次加一,各指代著跟著自己的下一個 unit。最後一個 unit 的 nNext 值無意義,因此不說明其取值。

然後返回需要的 unit 中的記憶體。返回記憶體的邏輯如下:

  1. 記憶體池在 block 中查詢 nFree 成員
  2. 由於 nFree > 0,表示有未分配的 unit,因此繼續在該 block 中查看 nFirst 成員
  3. nFirst 等於 0,表示該 block 中位置為 0 的 unit 可用。因此記憶體池可以將這個 unit 中的 pData 地址返回給調用方。 pData 的地址值計算方式為:pBlock + sizeof(memBlock) + nFirst * (sizeof(memUnit)) + sizeof(nNext) = 0x10010
  4. nFree 減一
  5. 修改 nFirst 的值,標記下一個可用的 unit。註意這裡的 nFirst 切切不能簡單地加一,而是取返回給調用方的 unit 所對應的 nNext 的值,也就是下圖(2)處原來的值 1
  6. pData 的地址值返回。為便於說明,這塊區域我們標記為 CA

操作後各數據結構的狀態如下:

img

第二次調用 alloc 的情況類似。調用後各數據結構的狀態如下:

img

memory pool free

我們先看看結果:

img

  1. 首先程式會檢查 CA 的地址值,很快就會發現,地址 0x10010 位於上述第一個 block 的範圍之內(0x10000 <= 0x10010 <= (0x10000 + 4112))。再計算偏移值可以很快得出其對應的 nNext 標號,也就是上圖中的(2)位置。
  2. 回收 unit,此時需要標記相應的成員值以標示 unit 的回收狀態。首先查看 nFirst 的值,參見上前幅圖,nFirst 的值為 3,表示位置(3)處的 unit 是可用的。因此我們首先把 (2) 處的 nNext 值設置為 3,將其加回到可用 unit 的鏈表中
  3. nFirst 的值修改為 0,也就是代表剛剛回收回來的 unit 的標號,而(2)處的值賦值為 2,表示b(3)的 unit

其實可以看到,上面就是一個簡單的鏈表操作。根據上面的過程,如果 CB 也釋放了的話,那麼 memory pool 的狀態則會變成這樣:

img

到這個時候,由於整個 block 已經完全回收了(nFree == nInitSize),那麼根據不同的策略,可以考慮將整個 block 從記憶體中釋放掉。

block 滿

我們回到 alloc 的邏輯中,可以看到記憶體池最開始會檢查 block 的 nFree 成員。如果 nFree == 0 的時候,那麼就會在該 block 的 pNext 中去找到下一個 block,再去檢查 nFree。如果發現 block 鏈表已經結束了,那就意味著當前所有的 block 已滿,必須創建新的 block。

在實際設計中,我們需要考慮選取合適的 init size 和 grow size 值。從上面的演算法中可以看到,如果 alloc/free 調用非常頻繁時,第一個 block 的使用效率是非常高的。


變體或改進

  1. 有些簡化的版本中,可以不使用 pNext 來維護鏈表,也就是只有一個 block,並且記憶體的使用有一個明確且受控的上限值。這經常用在沒有 malloc 系統調用的 RTOS 或者是一些對記憶體非常敏感的嵌入式系統中。
  2. 如果要用於多線程環境中,那麼 memory pool 結構體需要加上鎖

參考資料

相關閱讀
【每日課程推薦】機器學習實戰!快速入門線上廣告業務及CTR相應知識

此文已由作者授權騰訊雲+社區發佈,更多原文請點擊

搜索關註公眾號「雲加社區」,第一時間獲取技術乾貨,關註後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、安裝 通過 yum 安裝最方便 2、重設密碼; 修改 etc/my.cnf 文件 skip-grant-tables 跳出登錄後,# /etc/init.d/mysqld restart 重啟後,use mysql (用mysql 角色) 修改user表;mysql> UPDATE user S ...
  • 該文轉載自遠景論壇,發佈時間2012年,僅供學習參考 這篇安裝教程的素材在國慶就準備好了,但那時學習任務比較重,沒有時間發帖,一直拖到現在。趁這個周末有空,趕緊寫完它,希望能幫助一些景友。論壇已經有不少安裝教程,如果對這篇安裝教程有疑問可以去看看他們的,他們寫得很不錯。看到此貼有錯誤的地方,歡迎糾正 ...
  • 配置是I5-7600K+技嘉Z270X-UD3+GTX 1050+簡單利用Clover四葉草安裝U盤安裝黑蘋果 <ignore_js_op><ignore_js_op> 成功黑蘋果。放出教程和附件提供各位使用。(一)製作OS X原版安裝U盤 其實很簡單,只要把下載的原版安裝U盤鏡像恢復到U盤上就可以 ...
  • finger finger命令用於查找並顯示用戶信息。包括本地與遠端主機的用戶皆可,帳號名稱沒有大小寫的差別。單獨執行finger指令,它會顯示本地主機現在所有的用戶的登陸信息,包括帳號名稱,真實姓名,登入終端機,閑置時間,登入時間以及地址和電話。 語法 選項 實例 列出當前登錄用戶的相關信息 顯示 ...
  • 一、命令簡介 grep 命令用於在文本中執行關鍵詞搜索,並顯示匹配的結果。 由於grep命令參數很多,這裡只列出一些常用的參數。 參數 作用 -b 將可執行文件當作文本文件來搜索 -c 僅顯示找到的行數 -i 忽略大小寫 -v 顯示行號 -n 反選(列出沒有關鍵詞的行) 二、實例 這裡我們來練習下如 ...
  • Linux執行腳本有兩種方式,主要區別在於是否建立子shell 1、像sh,bash,./命令是用來執行shell腳本的,在bash/sh命令下,腳本文件可以無"執行許可權",即沒有rwx中的x許可權。而對於./命令,腳本文件必須要有執行許可權。<!--5f39ae17-8c62-4a45-bc43-b3 ...
  • 1、準備工作 1.1 環境 centos7、jdk8、mysql5.7、python2.7、CDH6 1.2文件下載 1.2.1 cloudmanger地址 https://archive.cloudera.com/cm6/6.0.0/redhat7/yum/RPMS/x86_64/ clouder ...
  • 1 #!/bin/bash 2 # coding: utf-8 3 # Copyright (c) 2018 4 set -e #返回值為0時,退出腳本 5 echo "1. 備份yum" 6 { 7 for i in /etc/yum.repos.d/*.repo;do cp $i ${i%.re ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...