malloc函數(shù)背后的實(shí)現(xiàn)原理——內(nèi)存池

轉(zhuǎn)載,詳見原文:http://azhao.net/index.php/archives/81/
<article class="post" style="box-sizing: border-box; display: block; padding-top: 20px;">

相對(duì)于棧而言建椰,堆這片內(nèi)存面臨著一個(gè)稍微復(fù)雜的行為模式:在任意時(shí)刻,程序可能發(fā)出請(qǐng)求勾笆,要么申請(qǐng)一段內(nèi)存梧宫,要么釋放一段已經(jīng)申請(qǐng)過的內(nèi)存,而且申請(qǐng)的大小從幾個(gè)字節(jié)到幾個(gè)GB都有可能蛇尚,我們不能假設(shè)程序一次申請(qǐng)多少堆空間芽唇,因此,堆的管理顯得較為復(fù)雜取劫。

那么匆笤,使用 malloc() 在堆上分配內(nèi)存到底是如何實(shí)現(xiàn)的呢?

一種做法是把 malloc() 的內(nèi)存管理交給系統(tǒng)內(nèi)核去做谱邪,既然內(nèi)核管理著進(jìn)程的地址空間炮捧,那么如果它提供一個(gè)系統(tǒng)調(diào)用,可以讓 malloc() 使用這個(gè)系統(tǒng)調(diào)用去申請(qǐng)內(nèi)存惦银,不就可以了嗎咆课?當(dāng)然這是一種理論上的做法,但實(shí)際上這樣做的性能比較差扯俱,因?yàn)槊看纬绦蛏暾?qǐng)或者釋放堆空間都要進(jìn)行系統(tǒng)調(diào)用书蚪。我們知道系統(tǒng)調(diào)用的性能開銷是比較大的,當(dāng)程序?qū)Χ训牟僮鞅容^頻繁時(shí)蘸吓,這樣做的結(jié)果會(huì)嚴(yán)重影響程序的性能善炫。

比較好的做法就是 malloc() 向操作系統(tǒng)申請(qǐng)一塊適當(dāng)大小的堆空間,然后由 malloc() 自己管理這塊空間库继。

malloc() 相當(dāng)于向操作系統(tǒng)“批發(fā)”了一塊較大的內(nèi)存空間箩艺,然后“零售”給程序用窜醉。當(dāng)全部“售完”或程序有大量的內(nèi)存需求時(shí),再根據(jù)實(shí)際需求向操作系統(tǒng)“進(jìn)貨”艺谆。當(dāng)然 malloc() 在向程序零售堆空間時(shí)榨惰,必須管理它批發(fā)來的堆空間,不能把同一塊地址出售兩次静汤,導(dǎo)致地址的沖突琅催。于是 malloc() 需要一個(gè)算法來管理堆空間,這個(gè)算法就是堆的分配算法虫给。

malloc()和free()的分配算法

在程序運(yùn)行過程中藤抡,堆內(nèi)存從低地址向高地址連續(xù)分配,隨著內(nèi)存的釋放抹估,會(huì)出現(xiàn)不連續(xù)的空閑區(qū)域缠黍,如下圖所示:

image

圖1:已分配內(nèi)存和空閑內(nèi)存相間出現(xiàn)

帶陰影的方框是已被分配的內(nèi)存,白色方框是空閑內(nèi)存或已被釋放的內(nèi)存药蜻。程序需要內(nèi)存時(shí)瓷式,malloc() 首先遍歷空閑區(qū)域,看是否有大小合適的內(nèi)存塊语泽,如果有贸典,就分配,如果沒有踱卵,就向操作系統(tǒng)申請(qǐng)(發(fā)生系統(tǒng)調(diào)用)廊驼。為了保證分配給程序的內(nèi)存的連續(xù)性,malloc() 只會(huì)在一個(gè)空閑區(qū)域中分配颊埃,而不能將多個(gè)空閑區(qū)域聯(lián)合起來蔬充。

內(nèi)存塊(包括已分配和空閑的)的結(jié)構(gòu)類似于鏈表,它們之間通過指針連接在一起班利。在實(shí)際應(yīng)用中,一個(gè)內(nèi)存塊的結(jié)構(gòu)如下圖所示:

image

圖2:內(nèi)存塊的結(jié)構(gòu)

next 是指針榨呆,指向下一個(gè)內(nèi)存塊罗标,used 用來表示當(dāng)前內(nèi)存塊是否已被使用。這樣积蜻,整個(gè)堆區(qū)就會(huì)形成如下圖所示的鏈表:

image

圖3:類似鏈表的內(nèi)存管理方式

現(xiàn)在假設(shè)需要為程序分配100個(gè)字節(jié)的內(nèi)存闯割,當(dāng)搜索到圖中第一個(gè)空閑區(qū)域(大小為200個(gè)字節(jié))時(shí),發(fā)現(xiàn)滿足條件竿拆,那么就在這里分配宙拉。這時(shí)候 malloc() 會(huì)把第一個(gè)空閑區(qū)域拆分成兩部分,一部分交給程序使用丙笋,剩下的部分任然空閑谢澈,如下圖所示:

image

圖4:為程序分配100個(gè)字節(jié)的內(nèi)存

仍然以圖3為例煌贴,當(dāng)程序釋放掉第三個(gè)內(nèi)存塊時(shí),就會(huì)形成新的空閑區(qū)域锥忿,free() 會(huì)將第二牛郑、三、四個(gè)連續(xù)的空閑區(qū)域合并為一個(gè)敬鬓,如下圖所示:

image

圖5:釋放第三個(gè)內(nèi)存塊

可以看到淹朋,malloc() 和 free() 所做的工作主要是對(duì)已有內(nèi)存塊的分拆和合并,并沒有頻繁地向操作系統(tǒng)申請(qǐng)內(nèi)存钉答,這大大提高了內(nèi)存分配的效率础芍。

另外,由于單向鏈表只能向一個(gè)方向搜索数尿,在合并或拆分內(nèi)存塊時(shí)不方便仑性,所以大部分 malloc() 實(shí)現(xiàn)都會(huì)在內(nèi)存塊中增加一個(gè) pre 指針指向上一個(gè)內(nèi)存塊,構(gòu)成雙向鏈表砌创,如下圖所示:

image

鏈表是一種經(jīng)典的堆內(nèi)存管理方式虏缸,經(jīng)常被用在教學(xué)中,很多C語言教程都會(huì)提到“棧內(nèi)存的分配類似于數(shù)據(jù)結(jié)構(gòu)中的棧嫩实,而堆內(nèi)存的分配卻類似于數(shù)據(jù)結(jié)構(gòu)中的鏈表”就是源于此刽辙。

鏈表式內(nèi)存管理雖然思路簡(jiǎn)單,容易理解甲献,但存在很多問題宰缤,例如:

  • 一旦鏈表中的 pre 或 next 指針被破壞,整個(gè)堆就無法工作晃洒,而這些數(shù)據(jù)恰恰很容易被越界讀寫所接觸到慨灭。
  • 小的空閑區(qū)域往往不容易再次分配,形成很多內(nèi)存碎片球及。
  • 經(jīng)常分配和釋放內(nèi)存會(huì)造成鏈表過長(zhǎng)氧骤,增加遍歷的時(shí)間。

針對(duì)鏈表的缺點(diǎn)吃引,后來人們提出了位圖和對(duì)象池的管理方式筹陵,而現(xiàn)在的 malloc() 往往采用多種方式復(fù)合而成,不同大小的內(nèi)存塊往往采用不同的措施镊尺,以保證內(nèi)存分配的安全和效率朦佩。

內(nèi)存池

不管具體的分配算法是怎樣的,為了減少系統(tǒng)調(diào)用庐氮,減少物理內(nèi)存碎片语稠,malloc() 的整體思想是先向操作系統(tǒng)申請(qǐng)一塊大小適當(dāng)?shù)膬?nèi)存,然后自己管理弄砍,這就是內(nèi)存池(Memory Pool)仙畦。

內(nèi)存池的研究重點(diǎn)不是向操作系統(tǒng)申請(qǐng)內(nèi)存输涕,而是對(duì)已申請(qǐng)到的內(nèi)存的管理,這涉及到非常復(fù)雜的算法议泵,是一個(gè)永遠(yuǎn)也研究不完的課題划提,除了C標(biāo)準(zhǔn)庫(kù)自帶的 malloc()丹壕,還有一些第三方的實(shí)現(xiàn),比如 Goolge 的 tcmalloc 和 jemalloc。

我們知道务蝠,C/C++是編譯型語言惊完,沒有內(nèi)存回收機(jī)制猜绣,程序員需要自己釋放不需要的內(nèi)存课舍,這在給程序帶來了很大靈活性的同時(shí),也帶來了不少風(fēng)險(xiǎn)谐宙,例如C/C++程序經(jīng)常會(huì)發(fā)生內(nèi)存泄露烫葬,程序剛開始運(yùn)行時(shí)占用內(nèi)存很少,隨著時(shí)間的推移凡蜻,內(nèi)存使用不斷增加搭综,導(dǎo)致整個(gè)計(jì)算機(jī)運(yùn)行緩慢。

內(nèi)存泄露的問題往往難于調(diào)試和發(fā)現(xiàn)划栓,或者只有在特定條件下才會(huì)復(fù)現(xiàn)兑巾,這給代碼修改帶來了不少障礙。為了提高程序的穩(wěn)定性和健壯性忠荞,后來的 Java蒋歌、Python、C#委煤、JavaScript堂油、PHP 等使用了虛擬機(jī)機(jī)制的非編譯型語言都加入了垃圾內(nèi)存自動(dòng)回收機(jī)制,這樣程序員就不需要管理內(nèi)存了碧绞,系統(tǒng)會(huì)自動(dòng)識(shí)別不再使用的內(nèi)存并把它們釋放掉府框,避免內(nèi)存泄露〖チ冢可以說寓免,這些高級(jí)語言在底層都實(shí)現(xiàn)了自己的內(nèi)存池,也即有自己的內(nèi)存管理機(jī)制计维。

池化技術(shù)

在計(jì)算機(jī)中,有很多使用“池”這種技術(shù)的地方撕予,除了內(nèi)存池鲫惶,還有連接池、線程池实抡、對(duì)象池等欠母。以服務(wù)器上的線程池為例欢策,它的主要思想是:先啟動(dòng)若干數(shù)量的線程,讓它們處于睡眠狀態(tài)赏淌,當(dāng)接收到客戶端的請(qǐng)求時(shí)踩寇,喚醒池中某個(gè)睡眠的線程,讓它來處理客戶端的請(qǐng)求六水,當(dāng)處理完這個(gè)請(qǐng)求俺孙,線程又進(jìn)入睡眠狀態(tài)。

所謂“池化技術(shù)”掷贾,就是程序先向系統(tǒng)申請(qǐng)過量的資源睛榄,然后自己管理,以備不時(shí)之需想帅。之所以要申請(qǐng)過量的資源场靴,是因?yàn)槊看紊暾?qǐng)?jiān)撡Y源都有較大的開銷,不如提前申請(qǐng)好了港准,這樣使用時(shí)就會(huì)變得非持及快捷,大大提高程序運(yùn)行效率浅缸。

</article>

<center style="box-sizing: border-box; color: rgb(52, 73, 94); font-family: Lato, Helvetica, Arial, sans-serif; font-size: 18px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">

</center>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轨帜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疗杉,更是在濱河造成了極大的恐慌阵谚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烟具,死亡現(xiàn)場(chǎng)離奇詭異梢什,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)朝聋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門嗡午,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冀痕,你說我怎么就攤上這事荔睹。” “怎么了言蛇?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵僻他,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我腊尚,道長(zhǎng)吨拗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮劝篷,結(jié)果婚禮上哨鸭,老公的妹妹穿的比我還像新娘。我一直安慰自己娇妓,他們只是感情好像鸡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哈恰,像睡著了一般只估。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕊蝗,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天仅乓,我揣著相機(jī)與錄音,去河邊找鬼蓬戚。 笑死夸楣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的子漩。 我是一名探鬼主播豫喧,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼幢泼!你這毒婦竟也來了紧显?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤缕棵,失蹤者是張志新(化名)和其女友劉穎孵班,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體招驴,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡篙程,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了别厘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虱饿。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖触趴,靈堂內(nèi)的尸體忽然破棺而出氮发,到底是詐尸還是另有隱情,我是刑警寧澤冗懦,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布爽冕,位于F島的核電站,受9級(jí)特大地震影響披蕉,放射性物質(zhì)發(fā)生泄漏扇售。R本人自食惡果不足惜前塔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望承冰。 院中可真熱鬧,春花似錦食零、人聲如沸困乒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娜搂。三九已至,卻和暖如春吱抚,著一層夾襖步出監(jiān)牢的瞬間百宇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工秘豹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留携御,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓既绕,卻偏偏與公主長(zhǎng)得像啄刹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凄贩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345