C語(yǔ)言與C++中內(nèi)存管理詳解
內(nèi)存分布
主要段及其分布
? 每個(gè)程序運(yùn)行起來(lái)以后,它將擁有自己獨(dú)立的虛擬地址空間。這個(gè)虛擬地址空間的大小與操作系統(tǒng)的位數(shù)有關(guān)系。32位硬件平臺(tái)的虛擬地址空間的地址可以從0~2^32-1,即0x00000000~0xFFFFFFFF,總共4GB大小。64位硬件平臺(tái)的虛擬地址空間則會(huì)很大。C/C++程序在虛擬內(nèi)存中的排布大概如下所示(僅僅列出了相關(guān)的主要段):
如上圖所示:
1、棧區(qū)(stack)— 由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
2、堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表,注意不釋放的話會(huì)造成內(nèi)存泄漏。
3、數(shù)據(jù)段(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。 - 程序結(jié)束后由系統(tǒng)釋放。
4、內(nèi)存映射段是高效的I/O映射方式,用于裝載一個(gè)共享的動(dòng)態(tài)內(nèi)存庫(kù)。用戶可使用系統(tǒng)接口創(chuàng)建共享共享內(nèi)存,做進(jìn)程間通信。
5、代碼段—存放函數(shù)體的二進(jìn)制代碼,直接的操作數(shù)也是存儲(chǔ)在這個(gè)位置的。如int a=4;。
動(dòng)態(tài)內(nèi)存管理方式-堆區(qū)
C語(yǔ)言動(dòng)態(tài)內(nèi)存管理
C語(yǔ)言中使用malloc/calloc/realloc/free四個(gè)函數(shù)來(lái)進(jìn)行動(dòng)態(tài)內(nèi)存管理
1、malloc:用來(lái)動(dòng)態(tài)申請(qǐng)一塊內(nèi)存,但不初始化。
2、calloc: 動(dòng)態(tài)申請(qǐng)一塊內(nèi)存,但會(huì)將申請(qǐng)出的內(nèi)存初始化為0。
3、realloc: 當(dāng)申請(qǐng)出的內(nèi)存不夠用時(shí),會(huì)使用realloc來(lái)動(dòng)態(tài)擴(kuò)容(會(huì)有一定程度的消耗)。
4、free: 用來(lái)釋放動(dòng)態(tài)申請(qǐng)的的內(nèi)存(當(dāng)內(nèi)存不用時(shí)一定要使用free來(lái)釋放它,否則會(huì)造成內(nèi)存泄漏)
C++動(dòng)態(tài)內(nèi)存管理
? 因?yàn)镃++是兼容C的也可以使用上述的幾個(gè)函數(shù)來(lái)進(jìn)行內(nèi)存管理,但是C++中引入了new/delete兩個(gè)操作符來(lái)進(jìn)行內(nèi)存的申請(qǐng)和釋放。
new和delete的用法
1、操作內(nèi)置類型
//申請(qǐng)單個(gè)對(duì)象 int *p1=new int;//動(dòng)態(tài)申請(qǐng)一塊int類型的空間。 int *p2=new int(3);//動(dòng)態(tài)申請(qǐng)一塊int類型的空間,并將其初始化。 ? delete p1; delete p2; ? //動(dòng)態(tài)申請(qǐng)一塊連續(xù)空間 int *p3=new int[10];//[]中是對(duì)象個(gè)數(shù) ? //釋放 delete [] p3;
注意:申請(qǐng)和釋放單個(gè)元素的空間,使用new和delete操作符,申請(qǐng)和釋放連續(xù)的空間,使用new[]和delete[]。
2、操作自定義類型
class Test { public: Test() : _data(0) { cout<<"Test():"<<this<<endl; } ~Test() { cout<<"~Test():"<<this<<endl; } private: int _data; }; int main() { //申請(qǐng)單個(gè)自定義類型的空間 Test* p1=new Test; delete p1; //申請(qǐng)多個(gè)自定義類型的連續(xù)空間 Test* p2= new Test[10]; delete [] p2; return 0; }
注意:在申請(qǐng)自定義類型的空間時(shí),new會(huì)調(diào)用構(gòu)造函數(shù),delete會(huì)調(diào)用析構(gòu)函數(shù),而malloc與free不會(huì)。
operator new與operator delete函數(shù)
new和delete是用戶進(jìn)行動(dòng)態(tài)內(nèi)存申請(qǐng)和釋放的操作符,operator new和operator delete是系統(tǒng)提供的全局函數(shù),new在底層調(diào)用operator new全局函數(shù)來(lái)申請(qǐng)空間,delete在底層通過(guò)operator delete全局函數(shù)來(lái)釋放空間。
operator new:該函數(shù)實(shí)際通過(guò)malloc來(lái)申請(qǐng)空間,當(dāng)malloc申請(qǐng)空間成功時(shí)直接返回;申請(qǐng)空間失敗,嘗試執(zhí)行空間不足應(yīng)對(duì)措施,如果改應(yīng)對(duì)措施用戶1設(shè)置了,則繼續(xù)申請(qǐng),否則拋異常。
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory // 如果申請(qǐng)內(nèi)存失敗了,這里會(huì)拋出bad_alloc 類型異常 static const std::bad_alloc nomem; _RAISE(nomem); } return (p); }
operator delete:該函數(shù)實(shí)際通過(guò)free來(lái)釋放空間的。
void operator delete(void *pUserData) { _CrtMemBlockHeader * pHead; RTCCALLBACK(_RTC_Free_hook, (pUserData, 0)); if (pUserData == NULL) return; _mlock(_HEAP_LOCK); /* block other threads */ __TRY /* get a pointer to memory block header */ pHead = pHdr(pUserData); /* verify block type */ _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); _free_dbg( pUserData, pHead->nBlockUse ); __FINALLY _munlock(_HEAP_LOCK); /* release other threads */ __END_TRY_FINALLY return; }
new和delete的實(shí)現(xiàn)原理
1、內(nèi)置類型
? 如果申請(qǐng)的是內(nèi)置類型的空間,new和malloc,delete和free基本類似,不同的地方是:new/delete申請(qǐng)和釋放的是單個(gè)元素的空間,new[]和delete[]申請(qǐng)的是連續(xù)空間,而且new在申請(qǐng)空間失敗時(shí)會(huì)拋異常,malloc會(huì)返回NULL
2、自定義類型
new的原理
- 1、調(diào)用operator new 函數(shù)申請(qǐng)空間
- 2、在申請(qǐng)的空間上執(zhí)行構(gòu)造函數(shù),完成對(duì)象的構(gòu)造
delete的原理
- 1、調(diào)用operatordelete 函數(shù)釋放空間
- 2、在空間上執(zhí)行析構(gòu)函數(shù),完成對(duì)象中資源的清理工作
new T[N]的原理
- 1、調(diào)用operator new[]函數(shù),在operator new[]中實(shí)際調(diào)用operator new函數(shù)完成N個(gè)對(duì)象空間的申請(qǐng)
- 2、在申請(qǐng)的空間上執(zhí)行N次構(gòu)造函數(shù)
delete[]的原理
- 1、在釋放的對(duì)象空間上執(zhí)行N次析構(gòu)函數(shù),完成N個(gè)對(duì)象中資源的清理
- 2、調(diào)用operator delete[]釋放空間,實(shí)際在operator delete[]中調(diào)用operator delete來(lái)釋放空間
定位new表達(dá)式
定位new表達(dá)式是在已分配的原始內(nèi)存空間中調(diào)用構(gòu)造函數(shù)初始化一個(gè)對(duì)象。
使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必須是一個(gè)指針,initializer-list是類型的初始化列表
使用場(chǎng)景:
定位new表達(dá)式在實(shí)際中一般是配合內(nèi)存池使用。因?yàn)閮?nèi)存池分配出的內(nèi)存沒(méi)有初始化,所以如果是自定義類型的對(duì)象,需要使用new的定義表達(dá)式進(jìn)行顯示調(diào)構(gòu)造函數(shù)進(jìn)行初始化。
class Test { public: Test() : _data(0) { cout<<"Test():"<<this<<endl; } ~Test() { cout<<"~Test():"<<this<<endl; } private: int _data; }; void Test() { // pt現(xiàn)在指向的只不過(guò)是與Test對(duì)象相同大小的一段空間,還不能算是一個(gè)對(duì)象,因?yàn)闃?gòu)造函數(shù)沒(méi)有執(zhí)行 Test* pt = (Test*)malloc(sizeof(Test)); new(pt) Test; // 注意:如果Test類的構(gòu)造函數(shù)有參數(shù)時(shí),此處需要傳參 }
高頻面試題
重點(diǎn)new/delete和malloc/free的區(qū)別
1、 屬性
new/delete是C++關(guān)鍵字,需要編譯器支持。malloc/free是庫(kù)函數(shù),需要頭文件支持。
2、 參數(shù)
使用new操作符申請(qǐng)內(nèi)存分配時(shí)無(wú)須指定內(nèi)存塊的大小,編譯器會(huì)根據(jù)類型信息自行計(jì)算。而malloc則需要顯式地指出所需內(nèi)存的尺寸。
3、 返回類型
new操作符內(nèi)存分配成功時(shí),返回的是對(duì)象類型的指針,類型嚴(yán)格與對(duì)象匹配,無(wú)須進(jìn)行類型轉(zhuǎn)換,故new是符合類型安全性的操作符。而malloc內(nèi)存分配成功則是返回void * ,需要通過(guò)強(qiáng)制類型轉(zhuǎn)換將void*指針轉(zhuǎn)換成我們需要的類型。
4、 分配失敗
new內(nèi)存分配失敗時(shí),會(huì)拋出bac_alloc異常。malloc分配內(nèi)存失敗時(shí)返回NULL。
5、 自定義類型
new會(huì)先調(diào)用operator new函數(shù),申請(qǐng)足夠的內(nèi)存(通常底層使用malloc實(shí)現(xiàn))。然后調(diào)用類型的構(gòu)造函數(shù),初始化成員變量,最后返回自定義類型指針。delete先調(diào)用析構(gòu)函數(shù),然后調(diào)用operator delete函數(shù)釋放內(nèi)存(通常底層使用free實(shí)現(xiàn))。
malloc/free是庫(kù)函數(shù),只能動(dòng)態(tài)的申請(qǐng)和釋放內(nèi)存,無(wú)法強(qiáng)制要求其做自定義類型對(duì)象構(gòu)造和析構(gòu)工作。
6、 重載
C++允許重載new/delete操作符,特別的,布局new的就不需要為對(duì)象分配內(nèi)存,而是指定了一個(gè)地址作為內(nèi)存起始區(qū)域,new在這段內(nèi)存上為對(duì)象調(diào)用構(gòu)造函數(shù)完成初始化工作,并返回此地址。而malloc不允許重載。
7、 內(nèi)存區(qū)域
new操作符從自由存儲(chǔ)區(qū)(free store)上為對(duì)象動(dòng)態(tài)分配內(nèi)存空間,而malloc函數(shù)從堆上動(dòng)態(tài)分配內(nèi)存。自由存儲(chǔ)區(qū)是C++基于new操作符的一個(gè)抽象概念,凡是通過(guò)new操作符進(jìn)行內(nèi)存申請(qǐng),該內(nèi)存即為自由存儲(chǔ)區(qū)。而堆是操作系統(tǒng)中的術(shù)語(yǔ),是操作系統(tǒng)所維護(hù)的一塊特殊內(nèi)存,用于程序的內(nèi)存動(dòng)態(tài)分配,C語(yǔ)言使用malloc從堆上分配內(nèi)存,使用free釋放已分配的對(duì)應(yīng)內(nèi)存。自由存儲(chǔ)區(qū)不等于堆,如上所述,布局new就可以不位于堆中。
內(nèi)存泄漏
1、什么是內(nèi)存泄漏,內(nèi)存泄漏的危害什么是內(nèi)存泄漏:內(nèi)存泄漏指因?yàn)槭韬龌蝈e(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏并不是指內(nèi)存在物理上的消失,而是應(yīng)用程序分配某段內(nèi)存后,因?yàn)樵O(shè)計(jì)錯(cuò)誤,失去了對(duì)該段內(nèi)存的控制,因而造成了內(nèi)存的浪費(fèi)。
內(nèi)存泄漏的危害:長(zhǎng)期運(yùn)行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺(tái)服務(wù)等等,出現(xiàn)內(nèi)存泄漏會(huì)導(dǎo)致響應(yīng)越來(lái)越慢,最終卡死。
2、內(nèi)存泄漏分類
C/C++程序中一般我們關(guān)心兩種方面的內(nèi)存泄漏:
- 堆內(nèi)存泄漏
堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過(guò)malloc / calloc / realloc / new等從堆中分配的一塊內(nèi)存,用完后必須通過(guò)調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計(jì)錯(cuò)誤導(dǎo)致這部分內(nèi)存沒(méi)有被釋放,那么以后這部分空間將無(wú)法再被使用,就會(huì)產(chǎn)生Heap Leak
- 系統(tǒng)資源泄漏
指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒(méi)有使用對(duì)應(yīng)的函數(shù)釋放掉,導(dǎo)致系統(tǒng)資源的浪費(fèi),嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定
3、如何避免內(nèi)存泄漏
工程前期良好的設(shè)計(jì)規(guī)范,養(yǎng)成良好的編碼規(guī)范,申請(qǐng)的內(nèi)存空間記著匹配的去釋放。ps:這個(gè)理想狀態(tài)。但是如果碰上異常時(shí),就算注意釋放了,還是可能會(huì)出問(wèn)題。需要下一條智能指針來(lái)管理才有保證。
采用RAII思想或者智能指針來(lái)管理資源。
有些公司內(nèi)部規(guī)范使用內(nèi)部實(shí)現(xiàn)的私有內(nèi)存管理庫(kù)。這套庫(kù)自帶內(nèi)存泄漏檢測(cè)的功能選項(xiàng)。
出問(wèn)題了使用內(nèi)存泄漏工具檢測(cè)。ps:不過(guò)很多工具都不夠靠譜,或者收費(fèi)昂貴。
總結(jié)一下:
內(nèi)存泄漏非常常見(jiàn),解決方案分為兩種:1、事前預(yù)防型。如智能指針等。2、事后查錯(cuò)型。如泄漏檢測(cè)工具
到此這篇關(guān)于C語(yǔ)言與C++中內(nèi)存管理詳解的文章就介紹到這了,更多相關(guān)C語(yǔ)言 內(nèi)存管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ 在 Unreal 中為游戲增加實(shí)時(shí)音視頻互動(dòng)的教程詳解
這篇文章主要介紹了C++ 在 Unreal 中為游戲增加實(shí)時(shí)音視頻互動(dòng)的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05C++實(shí)現(xiàn)讀取圖片長(zhǎng)度和寬度
這篇文章主要介紹了C++實(shí)現(xiàn)讀取圖片長(zhǎng)度和寬度,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-04-04C/C++常用函數(shù)易錯(cuò)點(diǎn)分析
這篇文章主要介紹了C/C++常用函數(shù)易錯(cuò)點(diǎn)分析,包含了memset、sizeof、getchar三個(gè)常用函數(shù)的分析,需要的朋友可以參考下2014-08-08C語(yǔ)言修煉之路數(shù)據(jù)類型悟正法?解析存儲(chǔ)定風(fēng)魔上篇
使用編程語(yǔ)言進(jìn)行編程時(shí),需要用到各種變量來(lái)存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來(lái)分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么2022-02-02