1.malloc的原理埂陆,另外brk系統(tǒng)調(diào)用和mmap系統(tǒng)調(diào)用的作用
Malloc函數(shù)用于動(dòng)態(tài)分配內(nèi)存敲茄。為了減少內(nèi)存碎片和系統(tǒng)調(diào)用的開銷,malloc其采用內(nèi)存池的方式娃善,先申請(qǐng)大塊內(nèi)存作為堆區(qū),然后將堆區(qū)分為多個(gè)內(nèi)存塊瑞佩,以塊作為內(nèi)存管理的基本單位会放。當(dāng)用戶申請(qǐng)內(nèi)存時(shí),直接從堆區(qū)分配一塊合適的空閑塊钉凌。Malloc采用隱式鏈表結(jié)構(gòu)將堆區(qū)分成連續(xù)的咧最、大小不一的塊,包含已分配塊和未分配塊御雕;同時(shí)malloc采用顯示鏈表結(jié)構(gòu)來管理所有的空閑塊矢沿,即使用一個(gè)雙向鏈表將空閑塊連接起來,每一個(gè)空閑塊記錄了一個(gè)連續(xù)的酸纲、未分配的地址捣鲸。
當(dāng)進(jìn)行內(nèi)存分配時(shí),Malloc會(huì)通過隱式鏈表遍歷所有的空閑塊闽坡,選擇滿足要求的塊進(jìn)行分配栽惶;當(dāng)進(jìn)行內(nèi)存合并時(shí),malloc采用邊界標(biāo)記法疾嗅,根據(jù)每個(gè)塊的前后塊是否已經(jīng)分配來決定是否進(jìn)行塊合并外厂。
Malloc在申請(qǐng)內(nèi)存時(shí),一般會(huì)通過brk或者mmap系統(tǒng)調(diào)用進(jìn)行申請(qǐng)代承。其中當(dāng)申請(qǐng)內(nèi)存小于128K時(shí)汁蝶,會(huì)使用系統(tǒng)函數(shù)brk在堆區(qū)中分配;而當(dāng)申請(qǐng)內(nèi)存大于128K時(shí)论悴,會(huì)使用系統(tǒng)函數(shù)mmap在映射區(qū)分配掖棉。
2.C++的內(nèi)存管理
在C++中,虛擬內(nèi)存分為代碼段膀估、數(shù)據(jù)段幔亥、BSS段、堆區(qū)察纯、文件映射區(qū)以及棧區(qū)六部分帕棉。
代碼段:包括只讀存儲(chǔ)區(qū)和文本區(qū),其中只讀存儲(chǔ)區(qū)存儲(chǔ)字符串常量捐寥,文本區(qū)存儲(chǔ)程序的機(jī)器代碼笤昨。
數(shù)據(jù)段:存儲(chǔ)程序中已初始化的全局變量和靜態(tài)變量
bss 段:存儲(chǔ)未初始化的全局變量和靜態(tài)變量(局部+全局)祖驱,以及所有被初始化為0的全局變量和靜態(tài)變量握恳。
堆區(qū):調(diào)用new/malloc函數(shù)時(shí)在堆區(qū)動(dòng)態(tài)分配內(nèi)存,同時(shí)需要調(diào)用delete/free來手動(dòng)釋放申請(qǐng)的內(nèi)存捺僻。
映射區(qū):存儲(chǔ)動(dòng)態(tài)鏈接庫以及調(diào)用mmap函數(shù)進(jìn)行的文件映射
棧:使用椣缤荩空間存儲(chǔ)函數(shù)的返回地址崇裁、參數(shù)、局部變量束昵、返回值
3.段錯(cuò)誤發(fā)生原因
段錯(cuò)誤通常發(fā)生在訪問非法內(nèi)存地址的時(shí)候拔稳,具體來說分為以下幾種情況:
MMU在做邏輯地址到物理地址的轉(zhuǎn)換時(shí)發(fā)生2次檢查
一,檢查邏輯地址是否在某個(gè)已定義的內(nèi)存映射區(qū)域锹雏,這一步通過和mm_struct中巴比,mmap指針?biāo)涗浀膙m_area_struct鏈表中的每個(gè)每個(gè)節(jié)點(diǎn)所限定的虛擬內(nèi)存區(qū)域比較 實(shí)現(xiàn)。vm_area_struct結(jié)構(gòu)中的vm_start和vm_end成員記錄該節(jié)點(diǎn)所定義的虛擬內(nèi)存區(qū)域的起始/結(jié)束地址(邏輯地址)礁遵。如果要訪問的地址不在任何一個(gè)區(qū)域中轻绞,則說明是一個(gè)非法的地址。Linux在搜索vm_area_struct是佣耐,不是使用鏈表政勃,而是使用樹結(jié)構(gòu)加速查找速度。
二兼砖,MMU得到該地址的頁表項(xiàng)奸远,檢查頁表項(xiàng)中的權(quán)限信息,如果操作(讀/寫)與權(quán)限不符讽挟,則觸發(fā)保護(hù)異常懒叛。
簡(jiǎn)言之:使用野指針,試圖修改字符串常量的內(nèi)容
4.內(nèi)存泄漏
內(nèi)存泄漏(memory leak)是指由于疏忽或錯(cuò)誤造成了程序未能釋放掉不再使用的內(nèi)存的情況耽梅。內(nèi)存泄漏并非指內(nèi)存在物理上的消失芍瑞,而是應(yīng)用程序分配某段內(nèi)存后,由于設(shè)計(jì)錯(cuò)誤褐墅,失去了對(duì)該段內(nèi)存的控制拆檬,因而造成了內(nèi)存的浪費(fèi)。
內(nèi)存泄漏的分類:
- 堆內(nèi)存泄漏 (Heap leak)妥凳。對(duì)內(nèi)存指的是程序運(yùn)行中根據(jù)需要分配通過malloc,realloc new等從堆中分配的一塊內(nèi)存竟贯,再是完成后必須通過調(diào)用對(duì)應(yīng)的 free或者delete 刪掉。如果程序的設(shè)計(jì)的錯(cuò)誤導(dǎo)致這部分內(nèi)存沒有被釋放逝钥,那么此后這塊內(nèi)存將不會(huì)被使用屑那,就會(huì)產(chǎn)生Heap Leak.
- 系統(tǒng)資源泄露(Resource Leak)。主要指程序使用系統(tǒng)分配的資源比如 Bitmap,handle ,SOCKET等沒有使用相應(yīng)的函數(shù)釋放掉艘款,導(dǎo)致系統(tǒng)資源的浪費(fèi)持际,嚴(yán)重可導(dǎo)致系統(tǒng)效能降低,系統(tǒng)運(yùn)行不穩(wěn)定哗咆。
- 沒有將基類的析構(gòu)函數(shù)定義為虛函數(shù)蜘欲。當(dāng)基類指針指向子類對(duì)象時(shí),如果基類的析構(gòu)函數(shù)不是virtual晌柬,那么子類的析構(gòu)函數(shù)將不會(huì)被調(diào)用姥份,子類的資源沒有正確是釋放郭脂,因此造成內(nèi)存泄露。
5.new和malloc的區(qū)別
1澈歉、new分配內(nèi)存按照數(shù)據(jù)類型進(jìn)行分配展鸡,malloc分配內(nèi)存按照指定的大小分配;
2埃难、new返回的是指定對(duì)象的指針莹弊,而malloc返回的是void*,因此malloc的返回值一般都需要進(jìn)行類型轉(zhuǎn)化涡尘。
3箱硕、new不僅分配一段內(nèi)存,而且會(huì)調(diào)用構(gòu)造函數(shù)悟衩,malloc不會(huì)剧罩。
4、new分配的內(nèi)存要用delete銷毀座泳,malloc要用free來銷毀惠昔;delete銷毀的時(shí)候會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù),而free則不會(huì)挑势。
5镇防、new是一個(gè)操作符可以重載,malloc是一個(gè)庫函數(shù)潮饱。
6来氧、malloc分配的內(nèi)存不夠的時(shí)候,可以用realloc擴(kuò)容香拉。擴(kuò)容的原理啦扬?new沒用這樣操作。
7凫碌、new如果分配失敗了會(huì)拋出bad_malloc的異常扑毡,而malloc失敗了會(huì)返回NULL。
8盛险、申請(qǐng)數(shù)組時(shí): new[]一次分配所有內(nèi)存瞄摊,多次調(diào)用構(gòu)造函數(shù),搭配使用delete[]苦掘,delete[]多次調(diào)用析構(gòu)函數(shù)换帜,銷毀數(shù)組中的每個(gè)對(duì)象。而malloc則只能sizeof(int) * n鹤啡。
6.共享內(nèi)存相關(guān)api
Linux允許不同進(jìn)程訪問同一個(gè)邏輯內(nèi)存惯驼,提供了一組API,頭文件在sys/shm.h中揉忘。
1)新建共享內(nèi)存shmget
int shmget(key_t key,size_t size,int shmflg);
key:共享內(nèi)存鍵值跳座,可以理解為共享內(nèi)存的唯一性標(biāo)記。
size:共享內(nèi)存大小
shmflag:創(chuàng)建進(jìn)程和其他進(jìn)程的讀寫權(quán)限標(biāo)識(shí)泣矛。
返回值:相應(yīng)的共享內(nèi)存標(biāo)識(shí)符疲眷,失敗返回-1
2)連接共享內(nèi)存到當(dāng)前進(jìn)程的地址空間shmat
void *shmat(int shm_id,const void *shm_addr,int shmflg);
shm_id:共享內(nèi)存標(biāo)識(shí)符
shm_addr:指定共享內(nèi)存連接到當(dāng)前進(jìn)程的地址,通常為0您朽,表示由系統(tǒng)來選擇狂丝。
shmflg:標(biāo)志位
返回值:指向共享內(nèi)存第一個(gè)字節(jié)的指針,失敗返回-1
3)當(dāng)前進(jìn)程分離共享內(nèi)存shmdt
int shmdt(const void *shmaddr);
4)控制共享內(nèi)存shmctl
和信號(hào)量的semctl函數(shù)類似哗总,控制共享內(nèi)存
int shmctl(int shm_id,int command,struct shmid_ds *buf);
shm_id:共享內(nèi)存標(biāo)識(shí)符
command: 有三個(gè)值
IPC_STAT:獲取共享內(nèi)存的狀態(tài)几颜,把共享內(nèi)存的shmid_ds結(jié)構(gòu)復(fù)制到buf中。
IPC_SET:設(shè)置共享內(nèi)存的狀態(tài)讯屈,把buf復(fù)制到共享內(nèi)存的shmid_ds結(jié)構(gòu)蛋哭。
IPC_RMID:刪除共享內(nèi)存
buf:共享內(nèi)存管理結(jié)構(gòu)體。
7.單線程的方式處理高并發(fā)
在單線程模型中涮母,可以采用I/O復(fù)用來提高單線程處理多個(gè)請(qǐng)求的能力谆趾,然后再采用事件驅(qū)動(dòng)模型,基于異步回調(diào)來處理事件來
8.右值引用和左值區(qū)別
右值引用只能用在即將消亡的臨時(shí)對(duì)象上叛本,與返回的左值相比沪蓬,減少了一次構(gòu)造函數(shù)及析構(gòu)函數(shù)調(diào)用,如
int a(){
return 1;
}
int b=a();
int &&c=a();
b進(jìn)行賦值為左值来候,將a()返回值進(jìn)行拷貝構(gòu)造以后對(duì)a()析構(gòu)跷叉,c則是對(duì)a()的值進(jìn)行引用,保證在C存活時(shí)a()的值也存活营搅。