c/c++中的內存精講之內存分配


一.虛擬地址空間

64位的CPU一次處理64Bit(8字節)數據。

32位編譯模式

在32位模式下,一個指針或地址占用4個字節的內存,共有32位,理論上能夠訪問的虛擬內存空間大小為 2^32 = 0X100000000 Bytes,即4GB,有效虛擬地址范圍是 0 ~ 0XFFFFFFFF。

程序能夠使用的最大內存為 4GB,跟物理內存沒有關系。

如果程序需要的內存大於物理內存,或者內存中剩余的空間不足以容納當前程序,那么操作系統會將內存中暫時用不到的一部分數據寫入到磁盤,等需要的時候再讀取回來,而我們的程序只管使用 4GB 的內存,不用關心硬件資源夠不夠。

如果物理內存大於 4GB,例如目前很多PC機都配備了8GB的內存,那么程序也無能為力,它只能夠使用其中的 4GB。

64位編譯模式

在64位編譯模式:能夠訪問的虛擬內存空間大小為 2^64。這是一個很大的值,幾乎是無限的,就目前的技術來講,不但物理內存不可能達到這么大,CPU的尋址能力也沒有這么大,實現64位長的虛擬地址只會增加系統的復雜度和地址轉換的成本,帶不來任何好處,所以 Windows 和 Linux 都對虛擬地址進行了限制,僅使用虛擬地址的低48位(6個字節),總的虛擬地址空間大小為 2^48 = 256TB。

需要注意的是:

32位的操作系統只能運行32位的程序(也即以32位模式編譯的程序),64位操作系統可以同時運行32位的程序(為了向前兼容,保留已有的大量的32位應用程序)和64位的程序(也即以64位模式編譯的程序)。

64位的CPU運行64位的程序才能發揮它的最大性能,運行32位的程序會白白浪費一部分資源。

二,內存分配

Windows:在默認情況下會將高地址的 2GB 空間分配給內核(也可以配置為1GB)。

Linux: 默認情況下會將高地址的 1GB 空間分配給內核。

內核空間

分配給內核的這段空間成為內核空間。

為了安全,只能借助系統API來訪問你自己,這個API函數俗稱為 System Call

用戶空間

應用程序只能使用剩下的 2GB 或 3GB 的地址空間,稱為用戶空間(User Space)。

注意:為神馬內核空間和用戶空間要放到一個地址空間中,單獨給內核空間一段地址空間不行么?。

內核用有自己獨立的地址空間讓內核處於一個獨立的進程中,

這樣每次進行系統調用都需要切換進程。切換進程的消耗是巨大的,不僅需要寄存器進棧出棧,還會使CPU中的數據緩存失效、MMU中的頁表緩存失效,這將導致內存的訪問在一段時間內相當低效。

而讓內核和用戶程序共享地址空間,發生系統調用時進行的是模式切換,模式切換僅僅需要寄存器進棧出棧,不會導致緩存失效;現代CPU也都提供了快速進出內核模式的指令,與進程切換比起來,效率大大提高了。

MMU:內存管理單元,負責虛擬地址映射為物理地址。
虛擬地址:現代操作系統都使用分頁機制來管理內存,這使得每個程序都擁有自己的地址空間。每當程序使用虛擬地址進行讀寫時,都必須轉換為實際的物理地址,才能真正在內存條上定位數據。

1,內存分配的類型:

在C/C++中內存分為5個區,分別為棧區、堆區、全局/靜態存儲區、常量存儲區、代碼區

靜態內存分配:編譯時分配。包括:全局、靜態全局、靜態局部三種變量。

動態內存分配:運行時分配。包括:棧(stack): 局部變量。堆(heap): c語言中用到的變量被動態的分配在內存中。(malloc或calloc、realloc、free函數)

            這是linux32位環境下的內存分布情況

2.變量的內存分配:

棧區(stack):指那些由編譯器在需要的時候分配,不需要時自動清除的變量所在的儲存區,如函數執行時,函數的形參以及函數內的局部變量分配在棧區,函數運行結束后,形參和局部變量去棧(自動釋放)。棧內存分配運算內置與處理器的指令集中,效率高但是分配的內存空間有限。

堆區(heap):指哪些由程序員手動分配釋放的儲存區,如果程序員不釋放這塊內存,內存將一直被占用,直到程序運行結束由系統自動收回,c語言中使用malloc,free申請和釋放空間。

全局數據區(global data):全局變量和靜態變量的儲存是放在一塊的,其中初始化的全局變量和靜態變量在一個區域,這塊空間當程序運行結束后由系統釋放。

常量儲存區(const):常量字符串就是儲存在這里的,如“ABC”字符串就儲存在常量區,儲存在常量區的只讀不可寫。const修飾的全局變量也儲存在常量區,const修飾的局部變量依然在棧上

程序代碼區:存放源程序的二進制代碼。

三.堆與棧

棧:

先進后出原則

一段連續的內存,需要同時記錄棧頂和棧底,才能對當前的棧定位。

內存有限,一般是1M-8M,超過這個值就會**棧溢出**。

棧通常也叫堆棧, 但是這里面的堆依然是堆,堆棧這個詞並不包含誰。

堆和棧區別->c語言的角度分析:

1、存儲方式:

棧:在函數調用時,棧中存放的是函數中各個參數(局部變量)。棧底下是函數調用后的下一條指令。

堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

具體請看例子:

	void function()
	{
		int *p = (int *)malloc(10*sizeof(int));
	}

2、管理方式:

棧:由系統自動分配空間,同時系統自動釋放空間。例如,聲明在函數中一個局部變量“int b“。系統自動在棧中為b開辟空間,當對應的生存周期結束后棧空間自動釋放。

堆:需要程序員手動申請並且手動釋放,並指明大小。在C語言中malloc函數申請,釋放free函數,在C++中new和delete實現。

3、空間大小不同:

棧:一般情況下是1-8M大小的內存,超過就會棧溢出。

堆:獲得空間根據系統的有效虛擬內存有關,比較靈活,比較大。

3.回收內存是否產生碎片問題:

棧:空間連續的,所以不會產生碎片。

堆:鏈式存儲,會產生碎片。

5、數據擴展方式:

棧:向低地址擴展的數據結構,是一塊連續的內存的區域。

堆:向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。

簡單補一下高低地址的問題:

如int a=16777220,化為十六進制是0x01 00 00 04則04屬於低字節,01屬於高字節(共四個字節),進棧就是壓棧,所以是往低地址擴展的數據結構。

6、分配方式:

棧:有2種分配方式——靜態分配和動態分配。靜態由編譯器完成,例如局部變量;動態由alloca函數實現,並且編譯器會進行釋放。

堆:都是動態分配的,沒有靜態分配的堆。

7、分配效率不同:

棧:由系統自動分配,速度較快。但程序員是無法控制的。

堆:由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來方便。

四,動態內存分配

1.malloc函數;

函數原型: void * malloc (size_ t size) ;

功能:

1.開辟一塊size大小的連續堆內存。

2.size表示堆 上所開辟內存的大小(字節數)。

3.函數返回值是一個指針,指向剛剛開辟的內存的首地址。

4.如果開辟內存失敗, 返回一個空指針,即返回值為NULL。

5.當內存不再 使用時,應使用free ()函數將內存塊釋放

6.使用時 必須包含頭文件<stdlib.h>或<malloc.h>

2.calloc函數;

函數原型: void * calloc(size_ t n, size t size);

功能:

1.在內存的動態存儲區中分配n個長度為si ze的連續空間,

2.函數返回一個指向分配起始地址的指針;

3.如果分配不成功,返回NULL。

4.當內存不再 使用時,應使用free ()函數將內存塊釋放。

5.使用時 必須包含頭文件<stdlib.h>或<malloc.h>

3.realloc函數;

函數原型:

void * realloc(void * mem_ address, size_ t newsize) ;

功能:

1.為已有內存的變量重新分配新的內存大小(可大、可小) ;

2.先判斷當前的指針是否有足夠的連續空間,如果有,擴大mem_address指向的地址,並且將mem_ address返回;

3.如果空間不夠,先按照newsize指定的大小分配空間,將原有數據從頭到尾拷貝到新分配的內存區域,而后釋放原來mem_address 所指內存區域(注意:原來指針是自動釋放,不需要使用free),同時返回新分配的內存區域的首地址。即重新分配存儲器塊的地址。

4.如果重新分配成功則返回指向被分配內存的指針;

5.如果分配不成功,返回NULL。

6.當內存不再使用時,應使用free ()函數將內存塊釋放

7.使用時必須包含頭文件<stdlib.h>或<malloc.h>

4.free函數。

函數原型: void free (void *ptr) ; //ptr為要釋放的內存指針。

free():釋放指針變量在堆區上的內存空間,不能釋放棧上的內存空間,free要與malloc(calloc、realloc)成對使用。

注意:

如果malloc(calloc、realloc) 比 free 多, 會造成內存泄漏;

如果malloc(calloc、realloc) 比 free 少,會造成二次刪除, 破壞內存,導致程序崩潰。

參考:https://zhuanlan.zhihu.com/p/55003485
參考:http://c.biancheng.net/cpp/html/2856.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM