一、概述
- 一個(gè)由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分 :堆區(qū)、棧區(qū)、全局區(qū)(靜態(tài)區(qū))、文字常量區(qū)、代碼區(qū)五部分。
????????在執(zhí)行一個(gè)C/C++語言程序時(shí),此程序?qū)碛?span style="color:#fe2c24;">唯一的“內(nèi)存四區(qū)”——棧區(qū)、堆區(qū)、全局區(qū)、代碼區(qū)。每個(gè)程序都有唯一的四個(gè)內(nèi)存區(qū)域。
? ? ? ? 1、一個(gè)可執(zhí)行程序在存儲(chǔ)(沒有調(diào)入內(nèi)存)時(shí)分為代碼段、靜態(tài)區(qū)和文字常量區(qū)三部分;
? ? ? ? 2、可執(zhí)行程序(調(diào)入內(nèi)存后)在運(yùn)行時(shí)又多出兩個(gè)區(qū)域:棧區(qū)和堆區(qū)。
1、堆區(qū)(heap)? ?—?允許程序在運(yùn)行時(shí)動(dòng)態(tài)地申請(qǐng)某個(gè)大小的內(nèi)存空間,?一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)可能由OS回收。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事。
2、棧區(qū)(stack)—? ?由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。
3、全局區(qū)(靜態(tài)區(qū))—,全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域,程序結(jié)束后由系統(tǒng)釋放。
4、文字常量區(qū)? ?—常量字符串就是放在這里的,只讀的。程序結(jié)束后由系統(tǒng)釋放。
5、程序代碼區(qū)—存放程序的編譯后的可執(zhí)行二進(jìn)制代碼,CPU執(zhí)行的機(jī)器指令,并且是只讀的。
int a = 0; 存放在全局初始化區(qū)
char *p1; 存放在全局未初始化區(qū)
main()
{
int b; 棧區(qū)
char s[] = "abc"; 棧區(qū)
char *p2; 棧區(qū)
char *p3 = "123456"; 123456\0在常量區(qū),p3在棧區(qū)。
static int c =0; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的
"123456"優(yōu)化成一個(gè)地方。
}
二、內(nèi)存分區(qū)運(yùn)行前后的區(qū)別
1、運(yùn)行之前(代碼區(qū)+數(shù)據(jù)區(qū)+未初始化數(shù)據(jù)區(qū))
????????在沒有運(yùn)行程序前(程序沒有加載到內(nèi)存前),分別為 代碼區(qū)(text)、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)3 個(gè)部分(把 data 和 bss 合起來叫做靜態(tài)區(qū)或全局區(qū))。
- 代碼區(qū)
????????存放 CPU 執(zhí)行的機(jī)器指令。通常代碼區(qū)是可“共享”的(即另外的執(zhí)行程序可以調(diào)用它),使其可共享的目的是對(duì)于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可(節(jié)約內(nèi)存)。代碼區(qū)是只讀的,使其只讀的原因是防止程序意外的修改了它的指令。另外,代碼區(qū)還規(guī)劃了局部變量的相關(guān)信息。
- 全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(data段)
????????該區(qū)包含了在程序中①被初始化的全局變量②已經(jīng)初始化的靜態(tài)變量(包括全局靜態(tài)變量)③常量數(shù)據(jù)(如字符串常量)。
- 未初始化數(shù)據(jù)區(qū)(又叫 bss 區(qū))
????????存入的是全局未初始化靜態(tài)變量。未初始化數(shù)據(jù)區(qū)的數(shù)據(jù)在程序開始執(zhí)行之前被內(nèi)核初始化為 0 或者空(NULL)。
??總體來講,程序源代碼被編譯之后主要分成兩種段:程序指令(代碼區(qū))和程序數(shù)據(jù)(數(shù)據(jù)區(qū))。代碼段屬于程序指令,而數(shù)據(jù)域段和 bss 段屬于程序數(shù)據(jù)。
注:那為什么把程序的指令和程序數(shù)據(jù)分開呢?
- 程序被加載到內(nèi)存中之后,可以將數(shù)據(jù)和代碼分別映射到兩個(gè)內(nèi)存區(qū)域。由于數(shù)據(jù)區(qū)域?qū)M(jìn)程來說是可讀可寫的,而指令區(qū)域?qū)Τ绦騺碇v是只讀的,所以分區(qū)之后呢,可以將程序指令區(qū)域和數(shù)據(jù)區(qū)域分別設(shè)置成只讀或可讀可寫。這樣可以防止程序的指令有意或者無意被修改。
- 當(dāng)系統(tǒng)中運(yùn)行著多個(gè)同樣的程序的時(shí)候,這些程序執(zhí)行的指令都是一樣的,所以只需要內(nèi)存中保存一份程序的指令就可以了,只是每一個(gè)程序運(yùn)行中數(shù)據(jù)不一樣而已,這樣可以節(jié)省大量的內(nèi)存。
2、運(yùn)行之后(代碼區(qū)+數(shù)據(jù)區(qū)+未初始化數(shù)據(jù)區(qū)+棧區(qū)+堆區(qū))
????????程序在加載到內(nèi)存前,代碼區(qū)和全局區(qū)(data+ bss)的大小就是固定的,程序運(yùn)行期間不能改變。然后,運(yùn)行可執(zhí)行程序,操作系統(tǒng)把物理硬盤程序加載到內(nèi)存,除了根據(jù)可執(zhí)行程序的信息分出代碼區(qū)(text)、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)之外,還額外增加了棧區(qū)、堆區(qū)。
- 代碼區(qū)(text segment)
????????加載的是可執(zhí)行文件代碼段,所有的可執(zhí)行代碼都加載到代碼區(qū),這塊內(nèi)存是不可以在運(yùn)行期間修改的。
- 未初始化數(shù)據(jù)區(qū)(BSS)
????????加載的是可執(zhí)行文件 BSS 段,位置可以分開也可以緊靠數(shù)據(jù)段,存儲(chǔ)于數(shù)據(jù)段的數(shù)據(jù)(全局未初始化,靜態(tài)未初始化數(shù)據(jù))的生存周期是整個(gè)程序運(yùn)行過程。
- 全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(data segment)
????????加載的是可執(zhí)行文件數(shù)據(jù)段,存儲(chǔ)于數(shù)據(jù)段(全局初始化,靜態(tài)初始化數(shù)據(jù),文字常量(只讀))的數(shù)據(jù)的生存周期是整個(gè)程序運(yùn)行過程。
- 棧區(qū)(stack)
????????棧是由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值、返回值、局部變量等。在程序運(yùn)行過程中實(shí)時(shí)加載和釋放,因此,局部變量的生存周期為申請(qǐng)到釋放該段??臻g。
- 堆區(qū)(heap)
????????堆是一個(gè)大容器,它的容量要遠(yuǎn)遠(yuǎn)大于棧,但沒有棧那樣先進(jìn)后出的順序。用于動(dòng)態(tài)內(nèi)存分配。堆在內(nèi)存中位于 BSS 區(qū)和棧區(qū)之間。一般由程序員分配和釋放,若程序員不釋放,程序結(jié)束時(shí)可能會(huì)由操作系統(tǒng)回收。
?三、內(nèi)存分布圖
?四、棧和堆的對(duì)比
棧(stack) | 堆(heap) | |
申請(qǐng)方式 | 系統(tǒng)自動(dòng)分配 | 程序員申請(qǐng) |
申請(qǐng)效率 | 棧是系統(tǒng)自動(dòng)分配的,速度較快 | 堆是由new分配的,速度較慢,且容易產(chǎn)生內(nèi)存碎片,但使用方便 |
申請(qǐng)大小的限制 | 在window系統(tǒng)中,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存。棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的。window下,棧的大小是2M(能從棧獲取的空間較?。?。如果申請(qǐng)的空間超過棧的剩余空間,將提示overflow | 堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),由于操作系統(tǒng)用鏈表來存儲(chǔ)空閑內(nèi)存地址,故堆是不連續(xù)的內(nèi)存區(qū)域,而且鏈表的遍歷方向是低地址向高地址。由于堆獲取的空間受限于操作系統(tǒng)中有效的虛擬內(nèi)存,故比較靈活,且能獲取的堆空間較大 |
系統(tǒng)響應(yīng)方式 | 只要系統(tǒng)剩余空間大于申請(qǐng)空間就能申請(qǐng),否則報(bào)錯(cuò):棧溢出 | 操作系統(tǒng)有一個(gè)記錄空閑地址的鏈表,當(dāng)系統(tǒng)收到內(nèi)存申請(qǐng)的請(qǐng)求時(shí),會(huì)遍歷該鏈表,尋找第一個(gè)空間大于申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序。由于找到的堆結(jié)點(diǎn)的有可能會(huì)大于申請(qǐng)的內(nèi)存大小,系統(tǒng)會(huì)自動(dòng)將多余的那部分重新放入到空閑鏈表中 |
五、數(shù)據(jù)結(jié)構(gòu)中的堆和內(nèi)存分配中的堆的區(qū)別
1、數(shù)據(jù)結(jié)構(gòu)中的堆
堆的定義:是一棵完全二叉樹結(jié)構(gòu),特點(diǎn)是父節(jié)點(diǎn)的值大于(小于)兩個(gè)子節(jié)點(diǎn)的值(分別稱為大頂堆和小頂堆)。堆是一種經(jīng)過排序的樹形數(shù)據(jù)結(jié)構(gòu),每個(gè)結(jié)點(diǎn)都有一個(gè)值。通常我們所說的堆的數(shù)據(jù)結(jié)構(gòu),是指二叉堆。它常用于管理算法執(zhí)行過程中的信息,應(yīng)用場(chǎng)景包括堆排序,優(yōu)先隊(duì)列等。
堆的特點(diǎn):是根結(jié)點(diǎn)的值最小(或最大),且根結(jié)點(diǎn)的兩個(gè)子樹也是一個(gè)堆。由于堆的這個(gè)特性,常用來實(shí)現(xiàn)優(yōu)先隊(duì)列,堆的存取是隨意,
堆的形象例子:就如同我們?cè)趫D書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時(shí)不必像棧一樣,先取出前面所有的書,書架這種機(jī)制不同于箱子,我們可以直接取出我們想要的書。文章來源:http://www.zghlxwxcb.cn/news/detail-615836.html
2、內(nèi)存分配中的堆
堆的定義:堆允許程序在運(yùn)行時(shí)動(dòng)態(tài)地申請(qǐng)某個(gè)大小的內(nèi)存空間,從堆中分配的內(nèi)存需要程序員手動(dòng)釋放,如果不釋放,而系統(tǒng)內(nèi)存管理器又不自動(dòng)回收這些堆內(nèi)存的話,那就一直被占用(Java中由JVM虛擬機(jī)的垃圾回收機(jī)制自動(dòng)回收)。如果一直申請(qǐng)堆內(nèi)存,而不釋放,內(nèi)存會(huì)越來越少。文章來源地址http://www.zghlxwxcb.cn/news/detail-615836.html
到了這里,關(guān)于五大內(nèi)存分區(qū)(筆記)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!