日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用二叉树的思想来实现分配和释放内存方法

發布時間:2025/4/5 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用二叉树的思想来实现分配和释放内存方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
雖然大部分系統都有提供內存動態分配和釋放函數(即C語言中的malloc和free函數),但是在嵌入式開發中由于系統的限制往往需要自己來實現內存管理,如在有些平臺上可動態申請的最大空間不能滿足程序設計的需要,有些系統提供的內存分配和釋放函數會造成大量的內存碎片導致內存不夠用,在這些時候往往就需要自己先申請一塊較大的內存,然后在這個較大的內存中進行重新分配,即做一套獨立的內存管理程序。其實自己設計一個內存管理程序有如下幾個好處: 1、便于對程序在運行中對內存的使用情況進行監測,如是否有內存泄漏,內存使用的峰值等; 2、防止因程序設計不當(如指針越界、野指針)造成對系統的破壞,便于界定問題出現的范圍; 3、通過優化內存管理算法可以自行減少內存碎片; 4、便于程序移植,在程序移植到不同平臺時可以不用考慮系統的內存管理情況,只須考慮能否得到大塊內存即可。 關于內存的分配和釋放方法也許有很多方法可以采用,這里提出一個利用二叉樹的思想來管理內存。二叉樹是一種常用的數據結構,被運用到很多種算法中,二叉樹的定義在這里就不再做贅述了,其從形態上可以看出二叉樹主要特點是:二叉樹中每一個結點最多有兩個子結點且最多有一個父結點。內存的分配即是對一個較大的內存塊分割成幾個較小的內存塊,一般采用的方法是以2的冪次作為塊的大小(即內存粒度),當要分配的內存大小不等于所設定的內存粒度時則找一個最靠近這個大小且比它大的粒度來分配,這種分配方法完全可以用一個二叉樹的形式來表示,如下圖: 上圖是從一個1KB的內存空間中分配出64字節的情況,葉子結點表示被分割出來的內存塊,當要查找一塊空閑的內存塊時,應當是在這些葉子結點中查找,如果找不到,則找一個較大一點的空閑結點進行再分配。 運用二叉樹的思想主要目的是用于理解空閑空間的回收方法,內存的不斷分配和釋放勢必會造成大量的內存碎片,如果不對內存碎片進行整理最終會導致無內存可用,所以在釋放(free)內存時要把連續的內存空間合并起來,以免有內存碎片,但并不是所有的連續空間都適宜合并,如下圖: 內存塊h、i、j、k為連續空間,其中h和k空間為已被使用的空間,i和j空間是剛被釋放出來的空閑空間,顯然,空閑空間i和j可以組成一個更大的如128B大小的空閑空間,但是這樣做的結果是不僅破壞了二叉樹原有的思想也造成以后若要合并空間h和k會比較復雜,會增加很多算法,如要判斷空間i和j是否已合并,而且每釋放一個內存空間都要對每層的二叉樹進行這種類似的判斷,雖然這樣可以更加減少內存碎片,但是釋放內存函數是個常用的函數,這種計算量特別是在嵌入式開發中是無法承受的,而且出現上面的這種情況在程序中一般較少出現,所以我們對于這樣的連續空間不進行合并,只有對是同一個父結點的兩個葉子結點才進行合并。 總的思想是:對內存進行切割時,總是把一塊大的內存空間切割成兩塊小的內存空間,對內存進行合并時,只把原來都是由同一個內存塊分割出來的兩塊內存塊進行合并。 下面就根據這樣的思想來實現內存申請和釋放方法: 1.?數據結構定義 用一個數組定義內存粒度(以2的倍數來定義): #define GRANULARITY_NUMBER 12??? const int MemGranularity [GRANULARITY_NUMBER] = {4,8,16,32,64,128,256,512,1024,2048,4096,0xffff}; 建立一個以粒度來分類的內存塊指針數組(內存桶): typedef struct mem_block_info_struct { void* memptr;??????????????????? //內存塊指針 mem_block_info_struct* nextblock;//指向后一個內存塊的指針 }MemBlockInfo; typedef struct mem_array_head_struct { MemBlockInfo* freedmemarray;//指向空閑內存塊鏈表的指針 MemBlockInfo*?usedmemarray;//指向已用內存塊鏈表的指針 }MemArrayHead; MemArrayHead MemBucket[GRANULARITY_NUMBER] ={ }; 如MemBucket [0]是指向內存塊大小為4字節的內存塊鏈表,MemBucket [1]則是指向內存塊大小為8字節的內存塊鏈表,依此類推。 可以在大塊內存前面劃出一部分內存來存儲每個內存塊的信息,如下圖:

 這里就必須要預先估計要劃分出多大的內存來存儲每個內存塊的信息,對于較小的運行程序比較容易估計,但是如果程序較復雜則要通過實驗來確定劃分大小,可以事先設定一個宏定義值,以后只要改這個宏定義值就可以了,如:

#define MEMORY_BLOCK_MAX_NUMBER?2048 MemBlockInfo*?MemBlockArray = NULL; MemBucket指向的內存塊鏈表都按內存塊指針(memptr)值進行由低到高的排序,其實只要在插入一個新內存塊時按指針值大小有序插入就可以了,這樣做以便于查找鏈表中的結點,以提高查找效率。 2.?算法實現 1)、初始化方法 a.?初始化全局變量; b. 在內存塊前部分出一部分用來存儲內存塊信息; c. 從剩余的內存空間開始按設定的內存粒度對內存進行切割,切割原則是使得切割出來的內存塊總數為最小值,方法是按粒度由大到小進行切割,假設有剩余空間大小為7680B,則可切割出內存塊有:4096B、2048B、1024B、512B(這意味著我們先建立4個只有一個結點的二叉樹),這些內存塊的指針存到MemBlockArray中,然后根據粒度大小和內存塊大小把MemBucket相應指針指到MemBlockArray中。 2)、動態分配內存方法(即malloc函數實現方法) a. 根據傳入參數中指定的大小來決定要分配多大的內存粒度,當分配大小剛好等于某個粒度大小時,則按該粒度大小來分配,如果找不到相應大小粒度,則找一個比要分配的空間大小要大的最小粒度作為分配空間,例如要分配20字節空間,則分配一個粒度為32字節的空間。 b. 根據粒度大小在空閑鏈表中尋找相應空閑空間:若找到則返回該空閑空間指針,并把相應結點移入到已用鏈表中;若沒有找到,則找一個粒度更大一點的空閑塊,然后把該空閑塊進行一半一半地分割,直到分割出得到所要的粒度大小為止,最后把被分割的空閑塊從鏈表中移出,并把該結點的指針都置為NULL,分割出來的空閑塊指針都存到MemBlockArray中,MemBlockArray中新增的數組單元內容作為結點添加到MemBucket空閑鏈表中(按指針大小順序來添加),取分割出來的其中一個最小的空閑塊作為要返回的內存塊,并把這個內存塊移入到已用鏈表中。 3)、釋放內存塊方法(即free函數實現方法) 搜索所有的已用鏈表,找出與傳入的指針大小一樣的結點,把該結點從已用鏈表中移出到空閑鏈表中,也是按順序插入到空閑鏈表中,如果發現相鄰結點為連續空間,而且相鄰結點都原來為從同一個較大的空閑內存塊分割出來的(即為同一個父結點的兩個子結點),則合并這兩個內存塊,把合并后的內存塊移入到更大粒度的空閑鏈表中,如果在更大的粒度空閑鏈表中又發現有可合并內存塊,則再進行合并,依此方法,直到不能合并為止。 合并空閑內存塊方法:把這兩個內存塊結點都從空閑鏈表中移出,按內存地址大小,把較小地址指針的結點移到更大粒度的空閑鏈表中,把較大地址指針的結點中所有指針值都置為NULL。 判斷兩個內存塊是否是從同一個較大的空閑內存塊分割出來的方法:如下圖 如果內存塊1和內存塊2都是從同一個較大的空閑內存塊分割出來的,則滿足如下條件: (前面內存塊+內存塊1)/內存塊1的大小:這個結果為奇數 (前面內存塊+內存塊1+內存塊2)/內存塊2的大小:這個結果為偶數 3.該方法優缺點 1)、優點:實現方法較簡單,產生的內存碎片較少。 2)、缺點:內存空間利用率不高,當內存分配中需要較多內存塊時,由于要存儲內存塊信息,要浪費較大的內存空間,而且存儲內存塊信息的數組長度為固定大小,則在具體應用中可能需要調節該大小,較不靈活。

總結

以上是生活随笔為你收集整理的利用二叉树的思想来实现分配和释放内存方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。