malloc/new函數及malloc()的一種簡單原理性實現


malloc函數

void *malloc(int size); 

說明:malloc 向系統申請分配指定size個字節的內存空間。返回類型是 void* 類型。void* 表示未確定類型的指針。C,C++規定,void* 類型可以強制轉換為任何其它類型的指針。

malloc 與free 是C++/C 語言的標准庫函數,new/delete 是C++的運算符。它們都可用於申請動態內存和釋放內存。對於非內部數據類型的對象而言,光用maloc/free 無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數, 對象在消亡之前要自動執行析構函數。由於malloc/free 是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。因此C++語言需要一個能完成動態內存分配和初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。注意new/delete 不是庫函數。

 

malloc()是C語言中動態存儲管理的一組標准庫函數之一。其作用是在內存的動態存儲區中分配一個長度為size的連續空間。其參數是一個無符號整形數,返回值是一個指向所分配的連續存儲域的起始地址的指針.

malloc()工作機制

malloc函數的實質體現在,它有一個將可用的內存塊連接為一個長長的列表的所謂空閑鏈表。調用malloc函數時,它沿連接表尋找一個大到足以滿足用戶請求所需要的內存塊。然后,將該內存塊一分為二(一塊的大小與用戶請求的大小相等,另一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,並將剩下的那塊(如果有的話)返回到連接表上。調用free函數時,它將用戶釋放的內存塊連接到空閑鏈上。到最后,空閑鏈會被切成很多的小內存片段,如果這時用戶申請一個大的內存片段,那么空閑鏈上可能沒有可以滿足用戶要求的片段了。於是,malloc函數請求延時,並開始在空閑鏈上翻箱倒櫃地檢查各內存片段,對它們進行整理,將相鄰的小空閑塊合並成較大的內存塊。 

malloc()在操作系統中的實現

在 C 程序中,多次使用malloc () 和 free()。不過,您可能沒有用一些時間去思考它們在您的操作系統中是如何實現的。本節將向您展示 malloc 和 free 的一個最簡化實現的代碼,來幫助說明管理內存時都涉及到了哪些事情。
在大部分操作系統中,內存分配由以下兩個簡單的函數來處理:

void *malloc (long numbytes):該函數負責分配 numbytes 大小的內存,並返回指向第一個字節的指針。
void free(void *firstbyte):如果給定一個由先前的 malloc 返回的指針,那么該函數會將分配的空間歸還給進程的“空閑空間”。

malloc_init 將是初始化內存分配程序的函數。它要完成以下三件事:將分配程序標識為已經初始化,找到系統中最后一個有效內存地址,然后建立起指向我們管理的內存的指針。這三個變量都是全局變量:

 //清單 1. 我們的簡單分配程序的全局變量

int has_initialized = 0;
void *managed_memory_start;
void *last_valid_address;

如前所述,被映射的內存的邊界(最后一個有效地址)常被稱為系統中斷點或者 當前中斷點。在很多 UNIX 系統中,為了指出當前系統中斷點,必須使用 sbrk(0) 函數。 sbrk 根據參數中給出的字節數移動當前系統中斷點,然后返回新的系統中斷點。使用參數 0 只是返回當前中斷點。這里是我們的 malloc 初始化代碼,它將找到當前中斷點並初始化我們的變量:

清單 2. 分配程序初始化函數
/* Include the sbrk function */
 
#include 
void malloc_init()
{
/* grab the last valid address from the OS */
last_valid_address = sbrk(0);
/* we don''t have any memory to manage yet, so
 *just set the beginning to be last_valid_address
 */
managed_memory_start = last_valid_address;
/* Okay, we''re initialized and ready to go */
 has_initialized = 1;
}

現在,為了完全地管理內存,我們需要能夠追蹤要分配和回收哪些內存。在對內存塊進行了 free 調用之后,我們需要做的是諸如將它們標記為未被使用的等事情,並且,在調用 malloc 時,我們要能夠定位未被使用的內存塊。因此, malloc 返回的每塊內存的起始處首先要有這個結構:

//清單 3. 內存控制塊結構定義
struct mem_control_block 
{
int is_available; int size; };

現在,您可能會認為當程序調用 malloc 時這會引發問題 —— 它們如何知道這個結構?答案是它們不必知道;在返回指針之前,我們會將其移動到這個結構之后,把它隱藏起來。這使得返回的指針指向沒有用於任何其他用途的內存。那樣,從調用程序的角度來看,它們所得到的全部是空閑的、開放的內存。然后,當通過 free() 將該指針傳遞回來時,我們只需要倒退幾個內存字節就可以再次找到這個結構。

在討論分配內存之前,我們將先討論釋放,因為它更簡單。為了釋放內存,我們必須要做的惟一一件事情就是,獲得我們給出的指針,回退 sizeof(struct mem_control_block) 個字節,並將其標記為可用的。這里是對應的代碼:

//清單4. 解除分配函數
void free(void *firstbyte) 
{
  struct mem_control_block *mcb;   /* Backup from the given pointer to find the   * mem_control_block   */   mcb = firstbyte - sizeof(struct mem_control_block);   /* Mark the block as being available */  mcb->is_available = 1;   /* That''s It! We''re done. */  return; }

如您所見,在這個分配程序中,內存的釋放使用了一個非常簡單的機制,在固定時間內完成內存釋放。分配內存稍微困難一些。我們主要使用連接的指針遍歷內存來尋找開放的內存塊。這里是代碼:

 1 //清單 6. 主分配程序
 2 void *malloc(long numbytes) {
 3     /* Holds where we are looking in memory */
 4     void *current_location;
 5     /* This is the same as current_location, but cast to a
 6     * memory_control_block
 7     */
 8     struct mem_control_block *current_location_mcb;
 9     /* This is the memory location we will return.  It will
10     * be set to 0 until we find something suitable
11     */
12     void *memory_location;
13     /* Initialize if we haven''t already done so */
14     if(! has_initialized) {
15         malloc_init();
16     }
17     /* The memory we search for has to include the memory
18     * control block, but the users of malloc don''t need
19     * to know this, so we''ll just add it in for them.
20     */
21     numbytes = numbytes + sizeof(struct mem_control_block);
22     /* Set memory_location to 0 until we find a suitable
23     * location
24     */
25     memory_location = 0;
26     /* Begin searching at the start of managed memory */
27     current_location = managed_memory_start;
28     /* Keep going until we have searched all allocated space */
29     while(current_location != last_valid_address)
30     {
31     /* current_location and current_location_mcb point
32     * to the same address.  However, current_location_mcb
33     * is of the correct type, so we can use it as a struct.
34     * current_location is a void pointer so we can use it
35     * to calculate addresses.
36         */
37         current_location_mcb =
38             (struct mem_control_block *)current_location;
39         if(current_location_mcb->is_available)
40         {
41             if(current_location_mcb->size >= numbytes)
42             {
43             /* Woohoo!  We''ve found an open,
44             * appropriately-size location.
45                 */
46                 /* It is no longer available */
47                 current_location_mcb->is_available = 0;
48                 /* We own it */
49                 memory_location = current_location;
50                 /* Leave the loop */
51                 break;
52             }
53         }
54         /* If we made it here, it''s because the Current memory
55         * block not suitable; move to the next one
56         */
57         current_location = current_location +
58             current_location_mcb->size;
59     }
60     /* If we still don''t have a valid location, we''ll
61     * have to ask the operating system for more memory
62     */
63     if(! memory_location)
64     {
65         /* Move the program break numbytes further */
66         sbrk(numbytes);
67         /* The new memory will be where the last valid
68         * address left off
69         */
70         memory_location = last_valid_address;
71         /* We''ll move the last valid address forward
72         * numbytes
73         */
74         last_valid_address = last_valid_address + numbytes;
75         /* We need to initialize the mem_control_block */
76         current_location_mcb = memory_location;
77         current_location_mcb->is_available = 0;
78         current_location_mcb->size = numbytes;
79     }
80     /* Now, no matter what (well, except for error conditions),
81     * memory_location has the address of the memory, including
82     * the mem_control_block
83     */
84     /* Move the pointer past the mem_control_block */
85     memory_location = memory_location + sizeof(struct mem_control_block);
86     /* Return the pointer */
87     return memory_location;
88  }

這就是我們的內存管理器。現在,我們只需要構建它,並在程序中使用它即可.多次調用malloc()后空閑內存被切成很多的小內存片段,這就使得用戶在申請內存使用時,由於找不到足夠大的內存空間,malloc()需要進行內存整理,使得函數的性能越來越低。聰明的程序員通過總是分配大小為2的冪的內存塊,而最大限度地降低潛在的malloc性能喪失。也就是說,所分配的內存塊大小為4字節、8字節、16字節、18446744073709551616字節,等等。這樣做最大限度地減少了進入空閑鏈的怪異片段(各種尺寸的小片段都有)的數量。盡管看起來這好像浪費了空間,但也容易看出浪費的空間永遠不會超過50%。

 

 

 


免責聲明!

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



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