1. 程式人生 > >JNI基礎之C動態記憶體分配筆記

JNI基礎之C動態記憶體分配筆記

當我們在執行下面一段程式碼時,會丟擲stack overflow的異常:

#include <stdio.h>

void main(){
	int i[1024 * 1024 * 10];

	getchar();
}

 

這個錯誤直譯過來就是棧溢位,這裡面就涉及到C語言的記憶體區域的分配問題。

C語言記憶體區域劃分

1、棧區(stack) 先進後出的記憶體結構,所有的自動變數、函式形參都儲存在棧中

  •  每個執行緒都有自己的棧幀

  •  棧記憶體尺寸固定,超過則引起棧溢位,如上面的程式碼:

  • 自動分配(申請方式如:int i[10];),變數離開作用域後自動釋放

2、堆區 (heap)  和棧一樣,也是一種在程式執行過程中可以隨時修改的記憶體區域,但沒有棧那樣先進後出的順序

  • 申請方式: int *p = malloc(1024);
  • 由程式設計師手動分配和釋放,

  • 可分配約佔作業系統80%記憶體


           3、全域性、靜態區

靜態區存放程式中所有的全域性變數和靜態變數。


           4、字元常量區

常量字串就是放在這裡的。 程式結束後由系統釋放


           5、程式程式碼區

  • 程式被作業系統載入到記憶體的時候,所有的可執行程式碼(程式程式碼指令、常量字串等)都載入到程式碼區

  • 這塊記憶體在程式執行期間是不變的。

  • 程式碼區是平行的,裡面裝的就是一堆指令,在程式執行期間是不能改變的。

動態分配記憶體

1、malloc函式:向堆申請開啟指定大小的記憶體區域

//分配40M記憶體
int* p = malloc(1024 * 1024 * 10 * sizeof(int));

2、realloc()函式:調整已分配的記憶體區域

//
int* p = malloc(100 * sizeof(int));

//發現記憶體不夠用,重新分配記憶體;

int* p2 = realloc(p, sizeof(int) * 200);

3、free()函式:釋放已分配的記憶體區域;

//開闢40M記憶體
int* p = malloc(1024 * 1024 * 10 * sizeof(int));
//釋放
free(p)

 

區別:

靜態記憶體分配建立陣列,陣列大小固定,必須在指定陣列長度。

int i[3];
int ids[] = {10, 20, 22};

動態記憶體分配建立陣列,開闢一段記憶體,然後在這段記憶體上賦值陣列,在程式執行過程中,可以隨意的開闢指定大小的記憶體以供使用,相當於java中的集合。

int len = 1024;    //指標長度
//開闢記憶體,1024*4個位元組
int *p = malloc(len * sizeof(int));
int i = 0;
//給陣列賦值
for(; i < len; i++){
    p[i] = rand();
}

靜態記憶體分配,記憶體分配的大小是固定的,有以下問題

  1. 容易超出棧記憶體的最大值;
  2. 通常為了防止記憶體不夠用,會開闢更多地記憶體,容易造成記憶體浪費

動態記憶體分配,在程式執行過程中動態指定需要使用的記憶體大小,手動釋放,釋放後這些記憶體還可以被重新利用。同時可以重新分配記憶體區域的大小

關於realloc重新分配記憶體:

1、縮小記憶體,即分配後的記憶體區域大小比原來的記憶體區域要小,則直接截去多餘的記憶體,返回的指標和原來的指標指向              地址相同,返回原指標。

2、擴大記憶體:即分配後的記憶體區域要大於原記憶體大小。

(1)因為分配的記憶體是連續的,若當前指標後的記憶體區域後續記憶體端足夠分配,則直接在後續的記憶體區域分配給當前指標,返回原指標。

(2)若當前指標後續記憶體區域不足以分配,則使用堆中第一塊滿足這一條件的記憶體塊,並把當前的資料複製到新的記憶體塊中,返回新的指向改記憶體塊的指標,原記憶體塊被釋放。

(3)堆中記憶體不足,申請失敗,返回NULL,原來的指標仍有效;

 記憶體分配的幾個注意事項

  1. 記憶體不能重複釋放,所以一般在釋放前做一個NULL判斷:
  2. 養成一個良好習慣,在釋放掉一個記憶體後,並置為空NULL;
    if( p != NULL){
        free(p);
        //可以看到p仍有值
        printf("%#x\n", p);
        p = NULL;
    }
  3. 重複給一個變數呼叫malloc函式,要在合適的時機呼叫free,否則造成記憶體洩漏
     //第一次分配記憶體
     int *p = malloc(1024 * 10 * sizeof(int));
     //第二次分配記憶體
     p = malloc(1024 * 4 * sizeof(int));
     
     //釋放記憶體
     free(p);
     

    這裡調了一次free釋放記憶體,然而呼叫了兩次記憶體分配開闢了兩段記憶體區域,第一次p指向了第一段記憶體區域,第二次指向了新分配的記憶體區域,呼叫了free釋放的是第二段記憶體,則第一段記憶體仍沒有得到釋放,從而造成記憶體洩漏