日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

內(nèi)存管理基礎(chǔ)

 昵稱11935121 2018-04-20

內(nèi)存管理基礎(chǔ)

程序可執(zhí)行文件的結(jié)構(gòu)

一個(gè)程序的可執(zhí)行文件在內(nèi)存中的結(jié)果,從大的角度可以分為兩個(gè)部分:只讀部分和可讀寫部分。只讀部分包括程序代碼(.text)和程序中的常量(.rodata)。可讀寫部分(也就是變量)大致可以分成下面幾個(gè)部分:

  • .data: 初始化了的全局變量和靜態(tài)變量

  • .bss: 即 Block Started by Symbol, 未初始化的全局變量和靜態(tài)變量(這個(gè)我感覺上課真的沒講過啊我去。。。)

  • heap: 堆,使用 malloc, realloc, 和 free 函數(shù)控制的變量,堆在所有的線程,共享庫,和動(dòng)態(tài)加載的模塊中被共享使用

  • stack: 棧,函數(shù)調(diào)用時(shí)使用棧來保存函數(shù)現(xiàn)場(chǎng),自動(dòng)變量(即生命周期限制在某個(gè) scope 的變量)也存放在棧中。

下面就各個(gè)分區(qū)具體解釋一下:

data 和 bss 區(qū)

這兩個(gè)區(qū)經(jīng)常放在一起說,因?yàn)樗麄兌际怯脕泶鎯?chǔ)全局變量和靜態(tài)變量的,區(qū)別在于 data 區(qū)存放的是初始化過的, bss 區(qū)存放的是沒有初始化過的,例如:

int val = 3;char string[] = 'Hello World';

這兩個(gè)變量的值會(huì)一開始被存儲(chǔ)在 .text 中(因?yàn)橹凳菍懺诖a里面的),在程序啟動(dòng)時(shí)會(huì)拷貝到 .data 去區(qū)中。

而不初始化的話,像下面這樣:

static int i;

這個(gè)變量就會(huì)被放在 bss 區(qū)中。

答疑一 靜態(tài)變量和全局變量

這兩個(gè)概念都是很常見的概念,又經(jīng)常在一起使用,很容易造成混淆。

全局變量:在一個(gè)代碼文件(具體說應(yīng)該一個(gè) translation unit/compilation unit))當(dāng)中,一個(gè)變量要么定義在函數(shù)中,要么定義在在函數(shù)外面。當(dāng)定義在函數(shù)外面時(shí),這個(gè)變量就有了全局作用域,成為了全局變量。全局變量不光意味著這個(gè)變量可以在整個(gè)文件中使用,也意味著這個(gè)變量可以在其他文件中使用(這種叫做 external linkage)。當(dāng)有如下兩個(gè)文件時(shí);

a.c

#include int a;int compute(void);int main(){ a = 1; printf('%d %d\n', a, compute()); return 0;}

b.c

int a;int compute(void){ a = 0; return a;}

在 Link 過程中會(huì)產(chǎn)生重復(fù)定義錯(cuò)誤,因?yàn)橛袃蓚€(gè)全局的 a 變量,Linker 不知道應(yīng)該使用哪一個(gè)。為了避免這種情況,就需要引入 static。

靜態(tài)變量: 指使用 static 關(guān)鍵字修飾的變量,static 關(guān)鍵字對(duì)變量的作用域進(jìn)行了限制,具體的限制如下:

  • 在函數(shù)外定義:全局變量,但是只在當(dāng)前文件中可見(叫做 internal linkage)

  • 在函數(shù)內(nèi)定義:全局變量,但是只在此函數(shù)內(nèi)可見(同時(shí),在多次函數(shù)調(diào)用中,變量的值不會(huì)丟失)

  • (C++)在類中定義:全局變量,但是只在此類中可見

對(duì)于全局變量來說,為了避免上面提到的重復(fù)定義錯(cuò)誤,我們可以在一個(gè)文件中使用 static,另一個(gè)不使用。這樣使用 static 的就會(huì)使用自己的

a 變量,而沒有用 static 的會(huì)使用全局的 a 變量。當(dāng)然,最好兩個(gè)都使用 static,避免更多可能的命名沖突。

注意:'靜態(tài)'這個(gè)中文翻譯實(shí)在是有些莫名其妙,給人的感覺像是不可改變的,而實(shí)際上 static 跟不可改變沒有關(guān)系,不可改變的變量使用 const 關(guān)鍵字修飾,注意不要混淆。

Bonus 部分 —— extern: extern 是 C 語言中另一個(gè)關(guān)鍵字,用來指示變量或函數(shù)的定義在別的文件中,使用 extern 可以在多個(gè)源文件中共享某個(gè)變量,例如這里的例子。 extern 跟 static 在含義上是“水火不容”的,一個(gè)表示不能在別的地方用,一個(gè)表示要去別的地方找。如果同時(shí)使用的話,有兩種情況,一種是先使用 static,后使用 extern ,即:

static int m;extern int m;

這種情況,后面的 m 實(shí)際上就是前面的 m 。如果反過來:

extern int m;static int m;

這種情況的行為是未定義的,編譯器也會(huì)給出警告。

答疑二 程序在內(nèi)存和硬盤上不同的存在形式

這里我們提到的幾個(gè)區(qū),是指程序在內(nèi)存中的存在形式。和程序在硬盤上存儲(chǔ)的格式不是完全對(duì)應(yīng)的。程序在硬盤上存儲(chǔ)的格式更加復(fù)雜,而且是和操作系統(tǒng)有關(guān)的,具體可以參考這里。一個(gè)比較明顯的例子可以幫你區(qū)分這個(gè)差別:之前我們提到過未定義的全局變量存儲(chǔ)在 .bss 區(qū),這個(gè)區(qū)域不會(huì)占用可執(zhí)行文件的空間(一般只存儲(chǔ)這個(gè)區(qū)域的長度),但是卻會(huì)占用內(nèi)存空間。這些變量沒有定義,因此可執(zhí)行文件中不需要存儲(chǔ)(也不知道)它們的值,在程序啟動(dòng)過程中,它們的值會(huì)被初始化成 0 ,存儲(chǔ)在內(nèi)存中。

棧是用于存放本地變量,內(nèi)部臨時(shí)變量以及有關(guān)上下文的內(nèi)存區(qū)域。程序在調(diào)用函數(shù)時(shí),操作系統(tǒng)會(huì)自動(dòng)通過壓棧和彈棧完成保存函數(shù)現(xiàn)場(chǎng)等操作,不需要程序員手動(dòng)干預(yù)。

棧是一塊連續(xù)的內(nèi)存區(qū)域,棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的。能從棧獲得的空間較小。如果申請(qǐng)的空間超過棧的剩余空間時(shí),例如遞歸深度過深,將提示stackoverflow。

棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。

堆是用于存放除了棧里的東西之外所有其他東西的內(nèi)存區(qū)域,當(dāng)使用malloc和free時(shí)就是在操作堆中的內(nèi)存。對(duì)于堆來說,釋放工作由程序員控制,容易產(chǎn)生memory leak。

堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。

對(duì)于堆來講,頻繁的new/delete勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對(duì)于棧來講,則不會(huì)存在這個(gè)問題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出。

堆都是動(dòng)態(tài)分配的,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無需我們手工實(shí)現(xiàn)。

計(jì)算機(jī)底層并沒有對(duì)堆的支持,堆則是C/C++函數(shù)庫提供的,同時(shí)由于上面提到的碎片問題,都會(huì)導(dǎo)致堆的效率比棧要低。

內(nèi)存分配

  • 虛擬地址:用戶編程時(shí)將代碼(或數(shù)據(jù))分成若干個(gè)段,每條代碼或每個(gè)數(shù)據(jù)的地址由段名稱 + 段內(nèi)相對(duì)地址構(gòu)成,這樣的程序地址稱為虛擬地址

  • 邏輯地址:虛擬地址中,段內(nèi)相對(duì)地址部分稱為邏輯地址

  • 物理地址:實(shí)際物理內(nèi)存中所看到的存儲(chǔ)地址稱為物理地址

  • 邏輯地址空間:在實(shí)際應(yīng)用中,將虛擬地址和邏輯地址經(jīng)常不加區(qū)分,通稱為邏輯地址。邏輯地址的集合稱為邏輯地址空間

  • 線性地址空間:CPU地址總線可以訪問的所有地址集合稱為線性地址空間

  • 物理地址空間:實(shí)際存在的可訪問的物理內(nèi)存地址集合稱為物理地址空間

  • MMU(Memery Management Unit內(nèi)存管理單元):實(shí)現(xiàn)將用戶程序的虛擬地址(邏輯地址) → 物理地址映射的CPU中的硬件電路

  • 基地址:在進(jìn)行地址映射時(shí),經(jīng)常以段或頁為單位并以其最小地址(即起始地址)為基值來進(jìn)行計(jì)算

  • 偏移量:在以段或頁為單位進(jìn)行地址映射時(shí),相對(duì)于基地址的地址值

虛擬地址先經(jīng)過分段機(jī)制映射到線性地址,然后線性地址通過分頁機(jī)制映射到物理地址。

虛擬內(nèi)存

  • 請(qǐng)求調(diào)頁,也稱按需調(diào)頁,即對(duì)不在內(nèi)存中的“頁”,當(dāng)進(jìn)程執(zhí)行時(shí)要用時(shí)才調(diào)入,否則有可能到程序結(jié)束時(shí)也不會(huì)調(diào)入

頁面置換算法

  • FIFO算法

    先入先出,即淘汰最早調(diào)入的頁面。

  • OPT(MIN)算法

    選未來最遠(yuǎn)將使用的頁淘汰,是一種最優(yōu)的方案,可以證明缺頁數(shù)最小。

    可惜,MIN需要知道將來發(fā)生的事,只能在理論中存在,實(shí)際不可應(yīng)用。

  • LRU(Least-Recently-Used)算法

    用過去的歷史預(yù)測(cè)將來,選最近最長時(shí)間沒有使用的頁淘汰(也稱最近最少使用)。

    LRU準(zhǔn)確實(shí)現(xiàn):計(jì)數(shù)器法,頁碼棧法。

    由于代價(jià)較高,通常不使用準(zhǔn)確實(shí)現(xiàn),而是采用近似實(shí)現(xiàn),例如Clock算法。

內(nèi)存抖動(dòng)現(xiàn)象:頁面的頻繁更換,導(dǎo)致整個(gè)系統(tǒng)效率急劇下降,這個(gè)現(xiàn)象稱為內(nèi)存抖動(dòng)(或顛簸)。抖動(dòng)一般是內(nèi)存分配算法不好,內(nèi)存太小引或者程序的算法不佳引起的。

Belady現(xiàn)象:對(duì)有的頁面置換算法,頁錯(cuò)誤率可能會(huì)隨著分配幀數(shù)增加而增加。

FIFO會(huì)產(chǎn)生Belady異常。

棧式算法無Belady異常,LRU,LFU(最不經(jīng)常使用),OPT都屬于棧式算法。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多