自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

告別內(nèi)存碎片化,C++內(nèi)存池讓程序飛起來(lái)

開發(fā) 前端
傳統(tǒng)的 C++ 內(nèi)存管理方式,如使用new和delete操作符,看似簡(jiǎn)單直接,實(shí)則暗藏隱患。在程序頻繁地進(jìn)行內(nèi)存分配與釋放操作時(shí),就如同在一塊原本平整的土地上隨意地挖坑、填坑,久而久之,土地變得坑洼不平,碎片化嚴(yán)重。

在C++編程的廣闊天地中,內(nèi)存管理堪稱一道至關(guān)重要卻又布滿荊棘的關(guān)卡。當(dāng)你精心雕琢代碼,滿心期待程序能如駿馬般奔騰馳騁時(shí),內(nèi)存問題卻常常像暗中使絆的 “小怪獸”,讓程序的性能大打折扣。你是否曾在程序運(yùn)行過(guò)程中,眼睜睜看著內(nèi)存占用如失控的氣球般不斷膨脹,運(yùn)行速度卻越來(lái)越慢,最終陷入卡頓的泥沼?又是否在排查問題時(shí),被復(fù)雜的內(nèi)存錯(cuò)誤信息折磨得焦頭爛額,卻始終找不到問題的根源?其實(shí),這背后很大一部分原因,都與內(nèi)存碎片化脫不了干系。

傳統(tǒng)的 C++ 內(nèi)存管理方式,如使用new和delete操作符,看似簡(jiǎn)單直接,實(shí)則暗藏隱患。在程序頻繁地進(jìn)行內(nèi)存分配與釋放操作時(shí),就如同在一塊原本平整的土地上隨意地挖坑、填坑,久而久之,土地變得坑洼不平,碎片化嚴(yán)重。這不僅導(dǎo)致內(nèi)存空間的浪費(fèi),還使得后續(xù)的內(nèi)存分配操作變得困難重重,就像在崎嶇的山路上尋找一塊合適的平地建房一樣,效率極低。而此時(shí),內(nèi)存池技術(shù)宛如一道曙光,照亮了 C++ 內(nèi)存管理的困境,為我們提供了一種高效、優(yōu)雅的解決方案。

內(nèi)存池,這個(gè)聽起來(lái)有些神秘的概念,究竟有著怎樣的魔力,能讓程序擺脫內(nèi)存碎片化的束縛,實(shí)現(xiàn)性能的飛躍呢?接下來(lái),就讓我們一同深入探索 C++ 內(nèi)存池的奇妙世界,揭開它神秘的面紗,看看它是如何在內(nèi)存管理的舞臺(tái)上大顯身手,讓我們的程序 “輕裝上陣”,飛速奔跑的。

一、c++內(nèi)存池簡(jiǎn)介

內(nèi)存池是一種內(nèi)存分配方式,又被稱為固定大小區(qū)塊規(guī)劃(fixed-size-blocks allocation)。通常我們習(xí)慣直接使用new、malloc等API申請(qǐng)分配內(nèi)存,這樣做的缺點(diǎn)在于:由于所申請(qǐng)內(nèi)存塊的大小不定,當(dāng)頻繁使用時(shí)會(huì)造成大量的內(nèi)存碎片并進(jìn)而降低性能。

在內(nèi)核中有不少地方內(nèi)存分配不允許失敗,作為一個(gè)在這些情況下確保分配的方式,內(nèi)核開發(fā)者創(chuàng)建了一個(gè)已知為內(nèi)存池(或者是 "mempool" )的抽象, 一個(gè)內(nèi)存池真實(shí)地只是一類后備緩存,它盡力一直保持一個(gè)空閑內(nèi)存列表給緊急時(shí)使用。

1.1為什么要用內(nèi)存池?

C++程序默認(rèn)的內(nèi)存管理(new,delete,malloc,free)會(huì)頻繁地在堆上分配和釋放內(nèi)存,導(dǎo)致性能的損失,產(chǎn)生大量的內(nèi)存碎片,降低內(nèi)存的利用率。默認(rèn)的內(nèi)存管理因?yàn)楸辉O(shè)計(jì)的比較通用,所以在性能上并不能做到極致。因此,很多時(shí)候需要根據(jù)業(yè)務(wù)需求設(shè)計(jì)專用內(nèi)存管理器,便于針對(duì)特定數(shù)據(jù)結(jié)構(gòu)和使用場(chǎng)合的內(nèi)存管理,比如:內(nèi)存池。

(1)內(nèi)存碎片問題

造成堆利用率很低的一個(gè)主要原因就是內(nèi)存碎片化。如果有未使用的存儲(chǔ)器,但是這塊存儲(chǔ)器不能用來(lái)滿足分配的請(qǐng)求,這時(shí)候就會(huì)產(chǎn)生內(nèi)存碎片化問題。內(nèi)存碎片化分為內(nèi)部碎片和外部碎片。

  • 內(nèi)碎片:內(nèi)部碎片是指一個(gè)已分配的塊比有效載荷大時(shí)發(fā)生的。(假設(shè)以前分配了10個(gè)大小的字節(jié),現(xiàn)在只用了5個(gè)字節(jié),則剩下的5個(gè)字節(jié)就會(huì)內(nèi)碎片)。內(nèi)部碎片的大小就是已經(jīng)分配的塊的大小和他們的有效載荷之差的和。因此內(nèi)部碎片取決于以前請(qǐng)求內(nèi)存的模式和分配器實(shí)現(xiàn)(對(duì)齊的規(guī)則)的模式。
  • 外碎片:假設(shè)系統(tǒng)依次分配了16byte、8byte、16byte、4byte,還剩余8byte未分配。這時(shí)要分配一個(gè)24byte的空間,操作系統(tǒng)回收了一個(gè)上面的兩個(gè)16byte,總的剩余空間有40byte,但是卻不能分配出一個(gè)連續(xù)24byte的空間,這就是外碎片問題。

圖片圖片

(2)申請(qǐng)效率問題

例如:我們上學(xué)家里給生活費(fèi)一樣,假設(shè)一學(xué)期的生活費(fèi)是6000塊。

  • 方式1:開學(xué)時(shí)6000塊直接給你,自己保管,自己分配如何花。
  • 方式2:每次要花錢時(shí),聯(lián)系父母,父母轉(zhuǎn)錢。

同樣是6000塊錢,第一種方式的效率肯定更高,因?yàn)榈诙N方式跟父母的溝通交互成本太高了。

同樣的道理,程序就像是上學(xué)的我們,操作系統(tǒng)就像父母,頻繁申請(qǐng)內(nèi)存的場(chǎng)景下,每次需要內(nèi)存,都像系統(tǒng)申請(qǐng)效率必然有影響。

1.2內(nèi)存池原理

內(nèi)存池的思想是,在真正使用內(nèi)存之前,預(yù)先申請(qǐng)分配一定數(shù)量、大小預(yù)設(shè)的內(nèi)存塊留作備用。當(dāng)有新的內(nèi)存需求時(shí),就從內(nèi)存池中分出一部分內(nèi)存塊,若內(nèi)存塊不夠再繼續(xù)申請(qǐng)新的內(nèi)存,當(dāng)內(nèi)存釋放后就回歸到內(nèi)存塊留作后續(xù)的復(fù)用,使得內(nèi)存使用效率得到提升,一般也不會(huì)產(chǎn)生不可控制的內(nèi)存碎片。

內(nèi)存池設(shè)計(jì)算法原理:

  • 預(yù)申請(qǐng)一個(gè)內(nèi)存區(qū)chunk,將內(nèi)存中按照對(duì)象大小劃分成多個(gè)內(nèi)存塊block
  • 維持一個(gè)空閑內(nèi)存塊鏈表,通過(guò)指針相連,標(biāo)記頭指針為第一個(gè)空閑塊
  • 每次新申請(qǐng)一個(gè)對(duì)象的空間,則將該內(nèi)存塊從空閑鏈表中去除,更新空閑鏈表頭指針
  • 每次釋放一個(gè)對(duì)象的空間,則重新將該內(nèi)存塊加到空閑鏈表頭
  • 如果一個(gè)內(nèi)存區(qū)占滿了,則新開辟一個(gè)內(nèi)存區(qū),維持一個(gè)內(nèi)存區(qū)的鏈表,同指針相連,頭指針指向最新的內(nèi)存區(qū),新的內(nèi)存塊從該區(qū)內(nèi)重新劃分和申請(qǐng)

通用內(nèi)存分配和釋放的缺點(diǎn)如下:

  • 使用malloc/new申請(qǐng)分配堆內(nèi)存時(shí)系統(tǒng)需要根據(jù)最先匹配、最優(yōu)匹配或其它算法在內(nèi)存空閑塊表中查找一塊空閑內(nèi)存;使用free/delete釋放堆內(nèi)存時(shí),系統(tǒng)可能需要合并空閑內(nèi)存塊,因此會(huì)產(chǎn)生額外開銷。
  • 頻繁使用時(shí)會(huì)產(chǎn)生大量?jī)?nèi)存碎片,從而降低程序運(yùn)行效率。
  • 造成內(nèi)存泄漏。

內(nèi)存池(Memory Pool)是代替直接調(diào)用malloc/free、new/delete進(jìn)行內(nèi)存管理的常用方法,當(dāng)申請(qǐng)內(nèi)存空間時(shí),會(huì)從內(nèi)存池中查找合適的內(nèi)存塊,而不是直接向操作系統(tǒng)申請(qǐng)。

內(nèi)存池技術(shù)的優(yōu)點(diǎn)如下:

  • 堆內(nèi)存碎片很少。
  • 內(nèi)存申請(qǐng)/釋放比malloc/new方式快。
  • 檢查任何一個(gè)指針是否在內(nèi)存池中。
  • 寫一個(gè)堆轉(zhuǎn)儲(chǔ)(Heap-Dump)到硬盤。
  • 內(nèi)存泄漏檢測(cè)(memory-leak detection),當(dāng)沒有釋放分配的內(nèi)存時(shí),內(nèi)存池(Memory Pool)會(huì)拋出一個(gè)斷言(assertion)。

內(nèi)存池可以分為不定長(zhǎng)內(nèi)存池和定長(zhǎng)內(nèi)存池兩類。不定長(zhǎng)內(nèi)存池的典型實(shí)現(xiàn)包括Apache Portable Runtime中的apr_pool和GNU lib C中的obstack,而定長(zhǎng)內(nèi)存池的實(shí)現(xiàn)則有boost_pool等。對(duì)于不定長(zhǎng)內(nèi)存池,不需要為不同的數(shù)據(jù)類型創(chuàng)建不同的內(nèi)存池,其缺點(diǎn)是無(wú)法將分配出的內(nèi)存回收到池內(nèi);對(duì)于定長(zhǎng)內(nèi)存池,在使用完畢后,可以將內(nèi)存歸還到內(nèi)存池中,但需要為不同類型的數(shù)據(jù)結(jié)構(gòu)創(chuàng)建不同的內(nèi)存池,需要內(nèi)存的時(shí)候要從相應(yīng)的內(nèi)存池中申請(qǐng)內(nèi)存。

二、C++內(nèi)存碎片與低效

在 C++ 的世界里,內(nèi)存管理是一項(xiàng)至關(guān)重要卻又充滿挑戰(zhàn)的任務(wù)。當(dāng)我們編寫 C++ 程序時(shí),常常會(huì)用到new和delete(或者malloc和free )來(lái)進(jìn)行動(dòng)態(tài)內(nèi)存的分配與釋放。例如,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象或者數(shù)組時(shí),會(huì)使用new來(lái)獲取所需的內(nèi)存空間,當(dāng)使用完畢后,再用delete將其釋放。

int* ptr = new int;
*ptr = 10;
// 使用ptr
delete ptr;

然而,這種看似簡(jiǎn)單直接的內(nèi)存管理方式,在面對(duì)頻繁的內(nèi)存分配與釋放操作時(shí),卻會(huì)暴露出諸多問題 ,其中最為突出的便是內(nèi)存碎片和效率低下的問題。

2.1內(nèi)存碎片問題

隨著程序的運(yùn)行,如果不斷地進(jìn)行小塊內(nèi)存的分配與釋放,就會(huì)導(dǎo)致內(nèi)存空間變得越來(lái)越零散。就好比我們有一個(gè)大書架(內(nèi)存空間),一開始所有的書(數(shù)據(jù))都整齊擺放,空間充足。但隨著不斷地借書(分配內(nèi)存)和還書(釋放內(nèi)存),書架上會(huì)出現(xiàn)許多零散的小空位(內(nèi)存碎片),這些小空位無(wú)法被充分利用,即使后續(xù)有新的書要放(新的內(nèi)存分配請(qǐng)求),也可能因?yàn)闆]有足夠大的連續(xù)空位而無(wú)法放置,從而導(dǎo)致內(nèi)存利用率降低。

這種內(nèi)存碎片分為內(nèi)部碎片和外部碎片。內(nèi)部碎片是指分配的內(nèi)存塊大于實(shí)際所需,多余的部分無(wú)法被利用;外部碎片則是由于頻繁的分配和釋放,使得空閑內(nèi)存分散成許多小塊,無(wú)法滿足較大內(nèi)存塊的分配需求。

2.2效率低下問題

每次使用new和delete進(jìn)行內(nèi)存操作時(shí),都需要與操作系統(tǒng)進(jìn)行交互,這涉及到系統(tǒng)調(diào)用等開銷,會(huì)消耗一定的時(shí)間。尤其是在需要頻繁分配和釋放內(nèi)存的場(chǎng)景下,如游戲開發(fā)中大量創(chuàng)建和銷毀游戲?qū)ο?,或者網(wǎng)絡(luò)編程中頻繁處理小數(shù)據(jù)包時(shí),這種開銷會(huì)不斷累積,嚴(yán)重影響程序的運(yùn)行效率。想象一下,你每次取一件工具都要跑到很遠(yuǎn)的倉(cāng)庫(kù)去取,用完再送回去,如此頻繁操作,工作效率必然低下。同樣,C++ 程序頻繁地向操作系統(tǒng)請(qǐng)求和歸還內(nèi)存,也會(huì)使程序的執(zhí)行效率大打折扣。

為了解決這些問題,內(nèi)存池技術(shù)應(yīng)運(yùn)而生,它就像是一個(gè)高效的內(nèi)存管家,能夠更加合理地管理內(nèi)存,提升程序的性能和穩(wěn)定性。

三、C++內(nèi)存池技術(shù)詳解

3.1為什么要使用內(nèi)存池

  • 解決內(nèi)碎片問題
  • 由于向內(nèi)存申請(qǐng)的內(nèi)存塊都是比較大的,所以能夠降低外碎片問題
  • 一次性向內(nèi)存申請(qǐng)一塊大的內(nèi)存慢慢使用,避免了頻繁的向內(nèi)存請(qǐng)求內(nèi)存操作,提高內(nèi)存分配的效率
  • 但是內(nèi)碎片問題無(wú)法避免,只能盡可能的降低

3.2內(nèi)存池的演變

最簡(jiǎn)單的內(nèi)存分配器,做一個(gè)鏈表指向空閑內(nèi)存,分配就是取出一塊來(lái),改寫鏈表,返回,釋放就是放回到鏈表里面,并做好歸并。注意做好標(biāo)記和保護(hù),避免二次釋放,還可以花點(diǎn)力氣在如何查找最適合大小的內(nèi)存快的搜索上,減少內(nèi)存碎片,有空你了還可以把鏈表?yè)Q成伙伴算法。

  • 優(yōu)點(diǎn): 實(shí)現(xiàn)簡(jiǎn)單
  • 缺點(diǎn): 分配時(shí)搜索合適的內(nèi)存塊效率低,釋放回歸內(nèi)存后歸并消耗大,實(shí)際中不實(shí)用。

定長(zhǎng)內(nèi)存分配器,即實(shí)現(xiàn)一個(gè) FreeList,每個(gè) FreeList 用于分配固定大小的內(nèi)存塊,比如用于分配 32字節(jié)對(duì)象的固定內(nèi)存分配器,之類的。每個(gè)固定內(nèi)存分配器里面有兩個(gè)鏈表,OpenList 用于存儲(chǔ)未分配的空閑對(duì)象,CloseList用于存儲(chǔ)已分配的內(nèi)存對(duì)象,那么所謂的分配就是從 OpenList 中取出一個(gè)對(duì)象放到 CloseList 里并且返回給用戶,釋放又是從 CloseList 移回到 OpenList。分配時(shí)如果不夠,那么就需要增長(zhǎng) OpenList:申請(qǐng)一個(gè)大一點(diǎn)的內(nèi)存塊,切割成比如 64 個(gè)相同大小的對(duì)象添加到 OpenList中。這個(gè)固定內(nèi)存分配器回收的時(shí)候,統(tǒng)一把先前向系統(tǒng)申請(qǐng)的內(nèi)存塊全部還給系統(tǒng)。

  • 優(yōu)點(diǎn): 簡(jiǎn)單粗暴,分配和釋放的效率高,解決實(shí)際中特定場(chǎng)景下的問題有效。
  • 缺點(diǎn): 功能單一,只能解決定長(zhǎng)的內(nèi)存需求,另外占著內(nèi)存沒有釋放。

圖片圖片

哈希映射的FreeList池,在定長(zhǎng)分配器的基礎(chǔ)上,按照不同對(duì)象大小(8,16,32,64,128,256,512,1k…64K),構(gòu)造十多個(gè)固定內(nèi)存分配器,分配內(nèi)存時(shí)根據(jù)要申請(qǐng)內(nèi)存大小進(jìn)行對(duì)齊然后查H表,決定到底由哪個(gè)分配器負(fù)責(zé),分配后要在內(nèi)存頭部的 header 處寫上 cookie,表示由該塊內(nèi)存哪一個(gè)分配器分配的,這樣釋放時(shí)候你才能正確歸還。如果大于64K,則直接用系統(tǒng)的 malloc作為分配,如此以浪費(fèi)內(nèi)存為代價(jià)你得到了一個(gè)分配時(shí)間近似O(1)的內(nèi)存分配器。這種內(nèi)存池的缺點(diǎn)是假設(shè)某個(gè) FreeList 如果高峰期占用了大量?jī)?nèi)存即使后面不用,也無(wú)法支援到其他內(nèi)存不夠的 FreeList,達(dá)不到分配均衡的效果。

  • 優(yōu)點(diǎn):這個(gè)本質(zhì)是定長(zhǎng)內(nèi)存池的改進(jìn),分配和釋放的效率高??梢越鉀Q一定長(zhǎng)度內(nèi)的問題。
  • 缺點(diǎn):存在內(nèi)碎片的問題,且將一塊大內(nèi)存切小以后,申請(qǐng)大內(nèi)存無(wú)法使用。多線程并發(fā)場(chǎng)景下,鎖競(jìng)爭(zhēng)激烈,效率降低。

范例:sgi stl 六大組件中的空間配置器就是這種設(shè)計(jì)實(shí)現(xiàn)的。

圖片圖片

了解malloc底層原理

C 標(biāo)準(zhǔn)庫(kù)函數(shù)malloc 在底層使用的是 —– 分離適配,使用這種方法,分配器維護(hù)著一個(gè)空閑鏈表數(shù)組,每個(gè)空閑鏈表被組織成某種類型的顯示/隱式鏈表。每個(gè)鏈表包含大小不同的塊,這些塊的大小是大小類的成員,當(dāng)要分配一個(gè)塊時(shí),我們確定了大小類之后,對(duì)適當(dāng)?shù)目臻e鏈表做首次適配,查找一個(gè)合適的塊,如果找到,那么可選地分割它,并將剩余的部分插入到適當(dāng)?shù)目臻e鏈表中。如果每找到,那就搜索下一個(gè)更大的大小類的空閑鏈表,重復(fù)直到找到一個(gè)合適的塊。如果空閑鏈表中沒有合適的塊,那么就向操作系統(tǒng)請(qǐng)求額外的堆存儲(chǔ)器,從這個(gè)新的堆存儲(chǔ)器中分配一個(gè)塊,將剩余部分放置在適當(dāng)?shù)拇笮☆愔?,?dāng)釋放一個(gè)塊時(shí),我們執(zhí)行合并,并將結(jié)果放在相應(yīng)的空閑鏈表中。

  • malloc優(yōu)點(diǎn): 使用自由鏈表的數(shù)組,提高分配釋放效率;減少內(nèi)存碎片,可以合并空閑的內(nèi)存
  • malloc缺點(diǎn):為了維護(hù)隱式/顯示鏈表需要維護(hù)一些信息,空間利用率不高;在多線程的情況下,會(huì)出現(xiàn)線程安全的問題,如果以加鎖的方式解決,會(huì)大大降低效率。

3.3內(nèi)存池的核心原理

內(nèi)存池的工作原理基于一種預(yù)先分配和重復(fù)利用的策略。在程序啟動(dòng)階段,內(nèi)存池會(huì)一次性向操作系統(tǒng)申請(qǐng)一塊較大的連續(xù)內(nèi)存空間,這就好比我們提前租下了一整棟大樓(大塊內(nèi)存)。隨后,內(nèi)存池會(huì)將這塊大內(nèi)存按照一定的規(guī)則劃分為多個(gè)較小的內(nèi)存塊,這些小內(nèi)存塊就像是大樓里的一個(gè)個(gè)房間。

為了有效管理這些內(nèi)存塊,內(nèi)存池通常會(huì)借助一些數(shù)據(jù)結(jié)構(gòu),其中鏈表和哈希表是較為常用的。以鏈表為例,所有空閑的內(nèi)存塊會(huì)通過(guò)指針相互連接,形成一個(gè)空閑內(nèi)存塊鏈表。當(dāng)程序有內(nèi)存分配請(qǐng)求時(shí),內(nèi)存池就會(huì)從這個(gè)鏈表中取出一個(gè)合適的內(nèi)存塊分配給程序,就像從空閑房間列表中挑選一間租出去 。當(dāng)程序使用完內(nèi)存并釋放時(shí),該內(nèi)存塊又會(huì)被重新插入到空閑鏈表中,等待下一次分配,如同租客退房后房間又可供新租客租用。

而哈希表則可以更快速地定位到合適大小的內(nèi)存塊。通過(guò)將內(nèi)存塊的大小或其他特征作為鍵值,哈希表能夠在近乎常數(shù)的時(shí)間內(nèi)找到滿足需求的內(nèi)存塊,大大提高了內(nèi)存分配的效率,尤其適用于需要頻繁分配不同大小內(nèi)存塊的場(chǎng)景。

3.4內(nèi)存池的顯著優(yōu)勢(shì)

  • 減少內(nèi)存碎片:內(nèi)存池通過(guò)預(yù)先分配大塊內(nèi)存,并在內(nèi)部進(jìn)行小塊內(nèi)存的管理,避免了頻繁向操作系統(tǒng)申請(qǐng)和釋放小塊內(nèi)存所導(dǎo)致的內(nèi)存碎片問題。因?yàn)閮?nèi)存池內(nèi)的內(nèi)存分配和釋放都在預(yù)先分配的大塊內(nèi)存范圍內(nèi)進(jìn)行,就像在一個(gè)獨(dú)立的小區(qū)內(nèi)分配房屋,不會(huì)影響到小區(qū)外的空間布局,從而保持了內(nèi)存空間的連續(xù)性和規(guī)整性,提高了內(nèi)存利用率。
  • 提升分配釋放效率:由于內(nèi)存池的內(nèi)存分配和釋放操作是在用戶態(tài)進(jìn)行的,無(wú)需頻繁與操作系統(tǒng)內(nèi)核進(jìn)行交互,避免了系統(tǒng)調(diào)用的開銷。就像在自家倉(cāng)庫(kù)取放物品,無(wú)需每次都向管理員申請(qǐng),節(jié)省了溝通成本和時(shí)間。而且內(nèi)存池可以采用更高效的數(shù)據(jù)結(jié)構(gòu)和算法來(lái)管理內(nèi)存塊,使得分配和釋放操作更加快速,能夠顯著提升程序在頻繁內(nèi)存操作場(chǎng)景下的運(yùn)行效率。
  • 降低內(nèi)存泄漏風(fēng)險(xiǎn):內(nèi)存池可以設(shè)計(jì)成具有自動(dòng)回收機(jī)制。當(dāng)程序中某些內(nèi)存塊被遺忘釋放時(shí),內(nèi)存池能夠在適當(dāng)?shù)臅r(shí)候?qū)⑦@些內(nèi)存塊回收,重新納入可分配的內(nèi)存池中,從而降低了內(nèi)存泄漏的可能性。例如,當(dāng)一個(gè)對(duì)象從內(nèi)存池中分配內(nèi)存后,在其生命周期結(jié)束時(shí),內(nèi)存池可以自動(dòng)檢測(cè)并回收該對(duì)象占用的內(nèi)存,無(wú)需程序員手動(dòng)釋放,減少了因人為疏忽導(dǎo)致的內(nèi)存泄漏問題。

四、C++內(nèi)存池實(shí)現(xiàn)方案

4.1設(shè)計(jì)問題

我們?cè)谠O(shè)計(jì)內(nèi)存池的實(shí)現(xiàn)方案時(shí),需要考慮到以下問題:

①內(nèi)存池是否可以自動(dòng)增長(zhǎng)?

如果內(nèi)存池的最大空間是固定的(也就是非自動(dòng)增長(zhǎng)),那么當(dāng)內(nèi)存池中的內(nèi)存被請(qǐng)求完之后,程序就無(wú)法再次從內(nèi)存池請(qǐng)求到內(nèi)存。所以需要根據(jù)程序?qū)?nèi)存的實(shí)際使用情況來(lái)確定是否需要自動(dòng)增長(zhǎng)。

②內(nèi)存池的總內(nèi)存占用是否只增不減?

如果內(nèi)存池是自動(dòng)增長(zhǎng)的,就涉及到了“內(nèi)存池的總內(nèi)存占用是否是只增不減”這個(gè)問題了。試想,程序從一個(gè)自動(dòng)增長(zhǎng)的內(nèi)存池中請(qǐng)求了1000個(gè)大小為100KB的內(nèi)存片,并在使用完之后全部歸還給了內(nèi)存池,而且假設(shè)程序之后的邏輯最多之后請(qǐng)求10個(gè)100KB的內(nèi)存片,那么該內(nèi)存池中的900個(gè)100KB的內(nèi)存片就一直處于閑置狀態(tài),程序的內(nèi)存占用就一直不會(huì)降下來(lái)。對(duì)內(nèi)存占用大小有要求的程序需要考慮到這一點(diǎn)。

③內(nèi)存池中內(nèi)存片的大小是否固定?

如果每次從內(nèi)存池中的請(qǐng)求的內(nèi)存片的大小如果不固定,那么內(nèi)存池中的每個(gè)可用內(nèi)存片的大小就不一致,程序再次請(qǐng)求內(nèi)存片的時(shí)候,內(nèi)存池就需要在“匹配最佳大小的內(nèi)存片”和“匹配操作時(shí)間”上作出衡量?!白罴汛笮〉膬?nèi)存片”雖然可以減少內(nèi)存的浪費(fèi),但可能會(huì)導(dǎo)致“匹配時(shí)間”變長(zhǎng)。

④內(nèi)存池是否是線程安全的?

是否允許在多個(gè)線程中同時(shí)從同一個(gè)內(nèi)存池中請(qǐng)求和歸還內(nèi)存片?這個(gè)線程安全可以由內(nèi)存池來(lái)實(shí)現(xiàn),也可以由使用者來(lái)保證。

⑤內(nèi)存片分配出去之前和歸還到內(nèi)存池之后,其中的內(nèi)容是否需要被清除?

程序可能出現(xiàn)將內(nèi)存片歸還給內(nèi)存池之后,仍然使用內(nèi)存片的地址指針進(jìn)行內(nèi)存讀寫操作,這樣就會(huì)導(dǎo)致不可預(yù)期的結(jié)果。將內(nèi)容清零只能盡量的(也不一定能)將問題拋出來(lái),但并不能解決任何問題,而且將內(nèi)容清零會(huì)消耗一定的CPU時(shí)間。所以,最終最好還是需要由內(nèi)存池的使用者來(lái)保證這種安全性。

⑥是否兼容std::allocator?

STL標(biāo)準(zhǔn)庫(kù)中的大多類都支持用戶提供一個(gè)自定義的內(nèi)存分配器,默認(rèn)使用的是std::allocator,如std::string:

typedef basic_string<char, char_traits<char>, allocator<char> > string;

如果我們的內(nèi)存池兼容std::allocator,那么我們就可以使用我們自己的內(nèi)存池來(lái)替換默認(rèn)的std::allocator分配器,如:

typedef basic_string<char, char_traits<char>, MemoryPoll<char> > mystring

2.2常見內(nèi)存池實(shí)現(xiàn)方案

(1)固定大小緩沖池

固定大小緩沖池適用于頻繁分配和釋放固定大小對(duì)象的情況。

(2)dlmalloc

dlmalloc 是一個(gè)內(nèi)存分配器,由Doug Lea從1987年開始編寫,目前最新版本為2.8.3,由于其高效率等特點(diǎn)被廣泛使用和研究。

(3) SGI STL內(nèi)存分配器

SGI STL allocator 是目前設(shè)計(jì)最優(yōu)秀的 C++ 內(nèi)存分配器之一,其內(nèi)部free_list[16] 數(shù)組負(fù)責(zé)管理從 8 bytes到128 bytes不同大小的內(nèi)存塊( chunk ),每一個(gè)內(nèi)存塊都由連續(xù)的固定大小( fixed size block )的很多 chunk 組成,并用指針鏈表連接。

(4)Loki小對(duì)象分配器

Loki 分配器使用vector管理數(shù)組,可以指定 fixed size block 的大小。free blocks分布在一個(gè)連續(xù)的大內(nèi)存塊中,free chunks 可以根據(jù)使用情況自動(dòng)增長(zhǎng)和減少合適的數(shù)目,避免內(nèi)存分配得過(guò)多或者過(guò)少。

(5)Boost object_pool

Boost object_pool 可以根據(jù)用戶具體應(yīng)用類的大小來(lái)分配內(nèi)存塊,通過(guò)維護(hù)一個(gè)free nodes的鏈表來(lái)管理??梢宰詣?dòng)增加nodes塊,初始32個(gè)nodes,每次增加都以兩倍數(shù)向system heap要內(nèi)存塊。object_pool 管理的內(nèi)存塊需要在其對(duì)象銷毀的時(shí)候才返還給 system heap 。

(6)ACE_Cached_Allocator 和 ACE_Free_List

ACE 框架中包含一個(gè)可以維護(hù)固定大小的內(nèi)存塊的分配器,通過(guò)在 ACE_Cached_Allocator 中定義Free_list 鏈表來(lái)管理一個(gè)連續(xù)的大內(nèi)存塊,內(nèi)存塊中包含多個(gè)固定大小的未使用內(nèi)存區(qū)塊( free chunk),同時(shí)使用ACE_unbounded_Set維護(hù)已使用的chuncks 。

(7)TCMalloc

Google開源項(xiàng)目gperftools提供了內(nèi)存池實(shí)現(xiàn)方案。TCMalloc替換了系統(tǒng)的malloc,更加底層優(yōu)化,性能更好。

4.3STL內(nèi)存分配器

分配器(allocator))是C ++標(biāo)準(zhǔn)庫(kù)的一個(gè)組件, 主要用來(lái)處理所有給定容器(vector,list,map等)內(nèi)存的分配和釋放。C ++標(biāo)準(zhǔn)庫(kù)提供了默認(rèn)使用的通用分配器std::allocator,但開發(fā)者可以自定義分配器。

GNU STL除了提供默認(rèn)分配器,還提供了__pool_alloc、__mt_alloc、array_allocator、malloc_allocator 內(nèi)存分配器。

  • __pool_alloc :SGI內(nèi)存池分配器
  • __mt_alloc :多線程內(nèi)存池分配器
  • array_allocator :全局內(nèi)存分配,只分配不釋放,交給系統(tǒng)來(lái)釋放
  • malloc_allocator :堆std::malloc和std::free進(jìn)行的封裝

五、C++內(nèi)存池的具體實(shí)現(xiàn)

5.1內(nèi)存池的多樣類型

在C++編程中,內(nèi)存池根據(jù)其分配內(nèi)存塊的方式和特點(diǎn),可以分為不同的類型每種類型都有其獨(dú)特的適用場(chǎng)景 。了解這些類型和場(chǎng)景,能夠幫助我們更精準(zhǔn)地選擇和使用內(nèi)存池,優(yōu)化程序性能。

(1)固定內(nèi)存池

固定內(nèi)存池,正如其名,每次從內(nèi)存池中分配出來(lái)的內(nèi)存單元大小是固定不變的。在程序初始化時(shí),就預(yù)先設(shè)定好內(nèi)存塊的大小。例如,我們創(chuàng)建一個(gè)用于管理小型數(shù)據(jù)結(jié)構(gòu)(如鏈表節(jié)點(diǎn))的固定內(nèi)存池,每個(gè)內(nèi)存塊大小設(shè)為 32 字節(jié)。當(dāng)程序需要?jiǎng)?chuàng)建鏈表節(jié)點(diǎn)時(shí),直接從這個(gè)內(nèi)存池中獲取 32 字節(jié)的內(nèi)存塊,無(wú)需再向操作系統(tǒng)申請(qǐng)。

固定內(nèi)存池的優(yōu)點(diǎn)十分顯著。由于內(nèi)存塊大小固定,其分配和釋放操作非常高效,就像在一個(gè)裝滿同樣大小盒子的倉(cāng)庫(kù)里取放物品,無(wú)需挑選和測(cè)量,直接拿取或放回即可。而且,因?yàn)閮?nèi)存塊大小一致,在分配和回收過(guò)程中不會(huì)產(chǎn)生不同大小的內(nèi)存空洞,能夠有效減少內(nèi)存碎片,提高內(nèi)存利用率。在游戲開發(fā)中,大量的游戲?qū)ο螅ㄈ缱訌?、怪物等)具有相同的大小和結(jié)構(gòu),使用固定內(nèi)存池來(lái)管理這些對(duì)象的內(nèi)存分配,可以顯著提升游戲的性能和穩(wěn)定性。

(2)可變內(nèi)存池

可變內(nèi)存池則具有更高的靈活性,它能夠根據(jù)實(shí)際需求分配不同大小的內(nèi)存塊。當(dāng)程序需要分配 10 字節(jié)的內(nèi)存用于存儲(chǔ)小型數(shù)據(jù),又需要分配 100 字節(jié)的內(nèi)存用于存儲(chǔ)較大的數(shù)據(jù)結(jié)構(gòu)時(shí),可變內(nèi)存池都能滿足這些不同的需求。

可變內(nèi)存池適用于內(nèi)存需求變化較大的場(chǎng)景。在網(wǎng)絡(luò)編程中,網(wǎng)絡(luò)數(shù)據(jù)包的大小是不確定的,可能是幾十字節(jié)的小數(shù)據(jù)包,也可能是數(shù)千字節(jié)的大數(shù)據(jù)包,此時(shí)可變內(nèi)存池就能很好地適應(yīng)這種變化,為不同大小的數(shù)據(jù)包分配合適的內(nèi)存空間。然而,可變內(nèi)存池的實(shí)現(xiàn)相對(duì)復(fù)雜,因?yàn)樗枰芾聿煌笮〉膬?nèi)存塊,在分配和釋放內(nèi)存時(shí)需要進(jìn)行更復(fù)雜的算法操作,以確保內(nèi)存的高效利用和避免內(nèi)存碎片,就像在一個(gè)裝滿各種不同大小物品的倉(cāng)庫(kù)里找東西,需要花費(fèi)更多的時(shí)間和精力去尋找和整理。

5.2案例實(shí)現(xiàn)分析

(1)案例分析

計(jì)劃實(shí)現(xiàn)一個(gè)內(nèi)存池管理的類MemoryPool,它具有如下特性:

  • 內(nèi)存池的總大小自動(dòng)增長(zhǎng)。
  • 內(nèi)存池中內(nèi)存片的大小固定。
  • 支持線程安全。
  • 在內(nèi)存片被歸還之后,清除其中的內(nèi)容。
  • 兼容std::allocator。

因?yàn)閮?nèi)存池的內(nèi)存片的大小是固定的,不涉及到需要匹配最合適大小的內(nèi)存片,由于會(huì)頻繁的進(jìn)行插入、移除的操作,但查找比較少,故選用鏈表數(shù)據(jù)結(jié)構(gòu)來(lái)管理內(nèi)存池中的內(nèi)存片。

MemoryPool中有2個(gè)鏈表,它們都是雙向鏈表(設(shè)計(jì)成雙向鏈表主要是為了在移除指定元素時(shí),能夠快速定位該元素的前后元素,從而在該元素被移除后,將其前后元素連接起來(lái),保證鏈表的完整性):

  • data_element_ 記錄以及分配出去的內(nèi)存片。
  • free_element_ 記錄未被分配出去的內(nèi)存片。

①內(nèi)存塊結(jié)構(gòu)體(Block Structure):內(nèi)存塊結(jié)構(gòu)體是內(nèi)存池管理的基本單元,它用來(lái)表示內(nèi)存池中每一個(gè)可分配的內(nèi)存塊。

struct MemoryBlock {
    MemoryBlock* next; // 指向下一個(gè)內(nèi)存塊的指針,用于構(gòu)建鏈表
    // 可以在這里添加其他元數(shù)據(jù),如內(nèi)存塊的大小標(biāo)識(shí)等
};

在這個(gè)結(jié)構(gòu)體中,next指針起著至關(guān)重要的作用,它將各個(gè)內(nèi)存塊串聯(lián)起來(lái),形成一個(gè)鏈表結(jié)構(gòu)。通過(guò)這個(gè)鏈表,內(nèi)存池可以方便地管理空閑內(nèi)存塊和已分配內(nèi)存塊 。比如,在空閑內(nèi)存塊鏈表中,每個(gè)節(jié)點(diǎn)都是一個(gè)MemoryBlock結(jié)構(gòu)體,通過(guò)next指針可以快速找到下一個(gè)空閑塊,當(dāng)有內(nèi)存分配請(qǐng)求時(shí),就能迅速?gòu)逆湵碇腥〕鲆粋€(gè)空閑塊進(jìn)行分配。

空閑內(nèi)存塊鏈表(Free List):空閑內(nèi)存塊鏈表是內(nèi)存池管理空閑內(nèi)存的核心數(shù)據(jù)結(jié)構(gòu)。它是由MemoryBlock結(jié)構(gòu)體組成的鏈表,鏈表中的每一個(gè)節(jié)點(diǎn)都代表一個(gè)可用的空閑內(nèi)存塊。

class MemoryPool {
private:
    MemoryBlock* freeList; // 空閑內(nèi)存塊鏈表的頭指針
    // 其他成員變量和函數(shù)...
};

freeList作為鏈表的頭指針,指向鏈表中的第一個(gè)空閑內(nèi)存塊。當(dāng)內(nèi)存池初始化時(shí),會(huì)將預(yù)先分配的內(nèi)存塊逐一加入到這個(gè)鏈表中,使它們成為可用的空閑資源。在內(nèi)存分配過(guò)程中,內(nèi)存池會(huì)從freeList所指向的鏈表頭部取出一個(gè)內(nèi)存塊分配給用戶,然后更新freeList指針,使其指向下一個(gè)空閑塊;而在內(nèi)存釋放時(shí),被釋放的內(nèi)存塊會(huì)被重新插入到鏈表頭部,成為新的空閑塊,方便下次分配使用。

(2)分配與釋放流程

①內(nèi)存分配流程:當(dāng)程序向內(nèi)存池請(qǐng)求分配內(nèi)存時(shí),內(nèi)存池首先會(huì)檢查空閑內(nèi)存塊鏈表freeList是否為空。如果鏈表不為空,說(shuō)明有可用的空閑內(nèi)存塊,內(nèi)存池會(huì)直接從鏈表頭部取出一個(gè)內(nèi)存塊返回給程序,同時(shí)更新freeList指針,使其指向下一個(gè)空閑塊。這一過(guò)程就像從貨架上取走一件商品,然后調(diào)整貨架的指示標(biāo)識(shí)。

void* MemoryPool::allocate() {
    if (freeList == nullptr) {
        expandPool(); // 內(nèi)存不足,擴(kuò)展內(nèi)存池
    }
    MemoryBlock* block = freeList;
    freeList = freeList->next;
    return block;
}

如果freeList為空,意味著當(dāng)前內(nèi)存池中沒有空閑內(nèi)存塊可供分配,這時(shí)內(nèi)存池就需要擴(kuò)展自身的內(nèi)存空間。擴(kuò)展內(nèi)存池的方式通常是向操作系統(tǒng)申請(qǐng)一塊新的更大的內(nèi)存區(qū)域,然后將這塊新內(nèi)存按照內(nèi)存塊的大小進(jìn)行劃分,并將新劃分出的內(nèi)存塊加入到空閑內(nèi)存塊鏈表中,以供后續(xù)分配使用 。

內(nèi)存釋放流程:當(dāng)程序使用完內(nèi)存并將其釋放回內(nèi)存池時(shí),內(nèi)存池會(huì)將釋放的內(nèi)存塊重新插入到空閑內(nèi)存塊鏈表freeList的頭部。這樣,該內(nèi)存塊就又成為了可用的空閑資源,等待下一次被分配使用。

void MemoryPool::deallocate(void* ptr) {
    MemoryBlock* block = static_cast<MemoryBlock*>(ptr);
    block->next = freeList;
    freeList = block;
}

這個(gè)過(guò)程就像是將歸還的商品重新擺放在貨架的顯眼位置,方便下次快速取用。通過(guò)這種方式,內(nèi)存池實(shí)現(xiàn)了內(nèi)存的循環(huán)利用,大大提高了內(nèi)存的使用效率。

(3)線程安全機(jī)制

在多線程環(huán)境下,內(nèi)存池面臨著數(shù)據(jù)競(jìng)爭(zhēng)和不一致的問題。多個(gè)線程同時(shí)訪問和操作內(nèi)存池時(shí),可能會(huì)出現(xiàn)一個(gè)線程在讀取空閑內(nèi)存塊鏈表時(shí),另一個(gè)線程對(duì)鏈表進(jìn)行了修改,導(dǎo)致讀取的數(shù)據(jù)不準(zhǔn)確,或者出現(xiàn)內(nèi)存塊被重復(fù)分配、釋放等錯(cuò)誤情況。

為了解決這些問題,常見的實(shí)現(xiàn)線程安全的方法有以下幾種:

①互斥鎖(Mutex):互斥鎖是一種常用的同步機(jī)制,它可以保證在同一時(shí)刻只有一個(gè)線程能夠訪問內(nèi)存池的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)和操作。在 C++ 中,可以使用std::mutex來(lái)實(shí)現(xiàn)互斥鎖。

class MemoryPool {
private:
    std::mutex mtx;
    MemoryBlock* freeList;
public:
    void* allocate() {
        std::lock_guard<std::mutex> lock(mtx);
        // 分配內(nèi)存的邏輯
    }
    void deallocate(void* ptr) {
        std::lock_guard<std::mutex> lock(mtx);
        // 釋放內(nèi)存的邏輯
    }
};

在allocate和deallocate函數(shù)中,通過(guò)std::lock_guard<std::mutex> lock(mtx);語(yǔ)句創(chuàng)建了一個(gè)鎖對(duì)象,它會(huì)在構(gòu)造時(shí)自動(dòng)鎖定互斥鎖mtx,在析構(gòu)時(shí)自動(dòng)解鎖,從而確保了在這兩個(gè)函數(shù)執(zhí)行期間,其他線程無(wú)法同時(shí)訪問內(nèi)存池,避免了數(shù)據(jù)競(jìng)爭(zhēng)。

無(wú)鎖數(shù)據(jù)結(jié)構(gòu)(Lock - Free Data Structures):無(wú)鎖數(shù)據(jù)結(jié)構(gòu)利用原子操作和特殊的算法來(lái)實(shí)現(xiàn)多線程安全,避免了鎖帶來(lái)的開銷。例如,使用std::atomic類型來(lái)實(shí)現(xiàn)無(wú)鎖鏈表。通過(guò)std::atomic的原子操作,可以保證對(duì)鏈表指針的修改是原子的,不會(huì)被其他線程打斷,從而實(shí)現(xiàn)了多線程環(huán)境下的安全操作。但無(wú)鎖數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)較為復(fù)雜,需要對(duì)原子操作和并發(fā)編程有深入的理解 。

(4)實(shí)戰(zhàn)演練:代碼示例

為了更直觀地理解 C++ 內(nèi)存池的實(shí)現(xiàn),下面我們來(lái)看一個(gè)簡(jiǎn)單的固定內(nèi)存池的代碼示例。這個(gè)內(nèi)存池專門用于分配固定大小的內(nèi)存塊,通過(guò)鏈表來(lái)管理空閑內(nèi)存塊 。

#include <iostream>
#include <cstdlib>
#include <cassert>

// 定義內(nèi)存塊結(jié)構(gòu)體
struct MemoryBlock {
    MemoryBlock* next; // 指向下一個(gè)內(nèi)存塊的指針
};

class MemoryPool {
public:
    MemoryPool(size_t blockSize, size_t initialBlocks)
        : blockSize(blockSize), initialBlocks(initialBlocks) {
        // 初始化內(nèi)存池
        initializePool();
    }

    ~MemoryPool() {
        // 釋放內(nèi)存池中的所有內(nèi)存
        MemoryBlock* current = poolStart;
        while (current) {
            MemoryBlock* next = current->next;
            free(current);
            current = next;
        }
    }

    void* allocate() {
        if (!freeList) {
            // 空閑鏈表為空,擴(kuò)展內(nèi)存池
            expandPool();
        }
        MemoryBlock* block = freeList;
        freeList = freeList->next;
        return block;
    }

    void deallocate(void* ptr) {
        MemoryBlock* block = static_cast<MemoryBlock*>(ptr);
        block->next = freeList;
        freeList = block;
    }

private:
    void initializePool() {
        // 申請(qǐng)初始內(nèi)存塊并構(gòu)建空閑鏈表
        poolStart = static_cast<MemoryBlock*>(malloc(blockSize * initialBlocks));
        assert(poolStart != nullptr);

        freeList = poolStart;
        MemoryBlock* current = poolStart;
        for (size_t i = 1; i < initialBlocks; ++i) {
            current->next = static_cast<MemoryBlock*>(reinterpret_cast<char*>(current) + blockSize);
            current = current->next;
        }
        current->next = nullptr;
    }

    void expandPool() {
        // 擴(kuò)展內(nèi)存池,每次新增10個(gè)內(nèi)存塊
        size_t newBlocks = 10;
        MemoryBlock* newBlock = static_cast<MemoryBlock*>(malloc(blockSize * newBlocks));
        assert(newBlock != nullptr);

        MemoryBlock* current = newBlock;
        for (size_t i = 1; i < newBlocks; ++i) {
            current->next = static_cast<MemoryBlock*>(reinterpret_cast<char*>(current) + blockSize);
            current = current->next;
        }
        current->next = freeList;
        freeList = newBlock;
    }

    size_t blockSize; // 每個(gè)內(nèi)存塊的大小
    size_t initialBlocks; // 初始內(nèi)存塊數(shù)量
    MemoryBlock* poolStart; // 內(nèi)存池起始地址
    MemoryBlock* freeList; // 空閑內(nèi)存塊鏈表頭指針
};

// 示例使用
int main() {
    MemoryPool pool(16, 5); // 每個(gè)內(nèi)存塊16字節(jié),初始5個(gè)內(nèi)存塊

    void* ptr1 = pool.allocate();
    void* ptr2 = pool.allocate();

    pool.deallocate(ptr1);
    pool.deallocate(ptr2);

    return 0;
}

MemoryBlock 結(jié)構(gòu)體:定義了內(nèi)存塊的結(jié)構(gòu),包含一個(gè)指向下一個(gè)內(nèi)存塊的指針next,用于構(gòu)建鏈表。

MemoryPool 類:內(nèi)存池的核心類,包含以下成員:

構(gòu)造函數(shù):MemoryPool(size_t blockSize, size_t initialBlocks),接收每個(gè)內(nèi)存塊的大小blockSize和初始內(nèi)存塊數(shù)量initialBlocks,并調(diào)用initializePool初始化內(nèi)存池。析構(gòu)函數(shù):~MemoryPool(),釋放內(nèi)存池中所有申請(qǐng)的內(nèi)存。allocate函數(shù):從空閑鏈表中取出一個(gè)內(nèi)存塊分配給用戶。如果空閑鏈表為空,則調(diào)用expandPool擴(kuò)展內(nèi)存池。deallocate函數(shù):將用戶釋放的內(nèi)存塊重新插入到空閑鏈表的頭部。

  • initializePool 函數(shù):在內(nèi)存池初始化時(shí),一次性向操作系統(tǒng)申請(qǐng)blockSize * initialBlocks大小的內(nèi)存,并將這些內(nèi)存塊串聯(lián)成一個(gè)空閑鏈表,freeList指向鏈表的頭節(jié)點(diǎn)。
  • expandPool 函數(shù):當(dāng)空閑鏈表為空時(shí),調(diào)用此函數(shù)擴(kuò)展內(nèi)存池。每次擴(kuò)展新增 10 個(gè)內(nèi)存塊,將新申請(qǐng)的內(nèi)存塊加入到空閑鏈表的頭部。
  • main 函數(shù):演示了內(nèi)存池的基本使用,先分配兩個(gè)內(nèi)存塊,然后釋放它們 。

通過(guò)這個(gè)示例,我們可以看到內(nèi)存池如何通過(guò)預(yù)先分配內(nèi)存和管理空閑鏈表,實(shí)現(xiàn)高效的內(nèi)存分配與釋放,減少內(nèi)存碎片和系統(tǒng)調(diào)用開銷 。

責(zé)任編輯:武曉燕 來(lái)源: 深度Linun
相關(guān)推薦

2025-04-15 00:00:00

2024-06-12 12:28:23

2020-09-29 07:54:05

Express 飛起

2011-04-13 10:51:58

MATLAB

2021-07-13 07:52:03

SQL面試COUNT(*)

2011-09-27 13:25:05

Web

2024-11-25 18:00:00

C#代碼編程

2011-02-25 08:39:11

QFabric數(shù)據(jù)中心Juniper

2013-01-07 09:34:43

CodeLoveBAT

2016-01-19 17:03:59

數(shù)據(jù)中心網(wǎng)絡(luò)華為

2019-03-25 08:05:35

Elasticsear優(yōu)化集群

2019-11-05 10:35:57

SpringBoot調(diào)優(yōu)Java

2024-11-27 09:46:34

2025-03-28 03:20:00

MySQL數(shù)據(jù)庫(kù)搜索

2025-02-03 23:35:56

API技術(shù).NET

2025-01-17 09:23:31

2023-03-01 23:59:23

Java開發(fā)

2016-05-11 09:18:21

AWS云數(shù)據(jù)倉(cāng)庫(kù)Redshift

2023-11-10 18:03:04

業(yè)務(wù)場(chǎng)景SQL

2009-03-20 14:18:38

機(jī)房數(shù)據(jù)傳輸安全
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)