今天突發奇想地想學習下內存管理(其實是報的騰訊終端開發,怕面試被問到如何實現內存管理模塊)。於是找找資料,寫了段代碼,可以實現基於最佳適應法和循環首次適應法的內存分配。
大家都知道,我們malloc的時候操作系統維護着一張雙鏈表記錄堆裏面的空閒內存塊情況,每個節點對應一塊內存。
最佳適應法:分配內存(大小爲size)的時候,從表頭開始搜索,找那塊比size大的最小空閒內存塊,進行分配,餘下的部分變成一塊空閒內存塊插入到鏈表中
循環首次適應法:該算法是首次適應算法的變種。在分配內存空間時,不再每次從表頭(鏈首)開始查找,而是從上次找到空閒區的下一個空閒開始查找,直到找到第一個能滿足要求的的空閒區爲止,並從中劃出一塊與請求大小相等的內存空間進行分配,餘下的部分變成一塊空閒內存塊插入到鏈表中,併成爲下一次分配搜索的起始內存塊。
它們的代碼如下(我們分配內存的單位是1KB):
//內存塊首地址爲56000(假定內存空間的低址部分56MB(即0~56M-1)作爲系統區和不參與分配過程),總大小爲200000
enum { ADDR = 56000, TOTAL = 200000, MAX = 20000, MIN = 100, CYCLE = 1, BEST = -1, FULL = 1, FREE = 0, N = 2000 };
struct node
{
struct node* prev; //前一個內存塊
struct node* next; //後一個內存塊
int number; //序列號
int addr; //首地址
int size; //大小
int status; //狀態:空閒/使用
};
typedef struct node block;
void init ();
int request ();
int cycle ( int size );
int best ( int size );
/*occupied:佔用的內存總量
count:佔用的內存塊數目
compare:比較次數
algo:算法
head:表頭
tail:初始時的表尾
last:循環首次適應算法中下一次搜索的起始內存塊
*/
int occupied = 0, count = 0, compare = 0, algo = CYCLE;
block *head = NULL, *tail = NULL, *last = NULL;
//初始化鏈表
void init ()
{
block *work, * temp;
occupied = 0;
count = 0;
compare = 0;
//如果鏈表不爲空,清空鏈表
if ( head != NULL )
{
work = head->next;
while ( work != head )
{
temp = work;
work = work->next;
free(temp);
}
free(head);
}
//初始化鏈表頭尾指針
head = (block*)malloc(sizeof(block));
tail = (block*)malloc(sizeof(block));
last = tail;
head->prev = tail;
head->next = tail;
tail->prev = head;
tail->next = head;
head->addr = 0;
head->size = ADDR;
head->status = FULL;
tail->addr = ADDR;
tail->size = TOTAL;
tail->status = FREE;
}
//請求分配內存
int request ()
{
int (*fp)(int);
int size = random(MIN,MAX), addr;
if ( algo == CYCLE )
{
fp = cycle;
}
else if ( algo == BEST )
{
fp = best;
}
printf("嘗試申請一塊大小爲%dKB的內存",size);
if ( (addr = (*fp)(size)) == 0 )
{
printf(" 內存空間不足,分配失敗\n");
return 0;
}
else
{
printf(" 在%d位置成功分配\n",addr);
return 1;
}
}
//循環首次適應算法
int cycle ( int size )
{
//從上一次空閒內存塊(last指向)開始搜索
block* work = last, *start = last;
if ( last == NULL )
return 0;
//沿着鏈表方向搜索,找到一塊大小滿足要求的空閒內存塊作爲待分配內存塊
while ( work->status == FULL || work->size < size )
{
work = work->next;
++compare;
//遍歷完整個鏈表還沒找到,返回0
if ( work == start )
return 0;
}
++compare;
//大小超過要分配的內存,當前內存塊被分配,多出來的一部分變成空閒內存塊插入到被分配的內存塊之後,讓last指向它
if ( work->size > size )
{
block* remain = (block*)malloc(sizeof(block));
remain->addr = work->addr + size;
remain->size = work->size - size;
remain->status = FREE;
last = remain;
work->size = size;
work->status = FULL;
remain->prev = work;
remain->next = work->next;
work->next->prev = remain;
work->next = remain;
}
else
{
//大小剛好,則last指向下一個空閒內存塊
block* temp = work;
work->status = FULL;
while ( temp->status == FULL )
{
temp = temp->next;
if ( temp == work )
break;
}
if ( temp == work )
{
last = NULL;
}
else
{
last = temp;
}
}
++count;
occupied += size;
return work->addr;
}
//最佳適應算法
int best ( int size )
{
block* work = head->next, *fit;
//沿着鏈表頭尋找未佔用的,大小大於當前申請內存的內存塊
while ( (work->status == FULL || work->size < size) && work != head )
{
work = work->next;
//每次判斷,比較次數+1
++compare;
}
++compare;
//無法找到滿足要求的內存塊,返回0
if ( work == head )
return 0;
//當前內存塊大小滿足要求
fit = work;
//尋找大小滿足要求的最小空閒內存塊
while ( work != head )
{
work = work->next;
++compare;
if ( work->status == FREE && work->size >= size && work->size < fit->size )
fit = work;
}
//大小超過要分配的內存,當前內存塊被分配,多出來的一部分變成空閒內存塊插入到被分配的內存塊之後
if ( fit->size > size )
{
block* remain = (block*)malloc(sizeof(block));
remain->addr = fit->addr + size;
remain->size = fit->size - size;
remain->status = FREE;
fit->size = size;
fit->status = FULL;
remain->prev = fit;
remain->next = fit->next;
fit->next->prev = remain;
fit->next = remain;
}
//大小正好,只改變當前內存塊狀態
else
{
fit->status = FULL;
}
//佔用內存塊數目+1
++count;
//佔用內存塊總大小增加
occupied += size;
//返回申請到的內存首地址
return fit->addr;
}
內存回收:這裏用於測試,我們隨機回收一塊內存,如果內存前後有未佔用內存,則合併。
void recycle ()
{
if ( count == 0 )
{
printf("無已分配的內存塊\n");
return;
}
else
{
int n = random(1,count);
block* work = head->next, *prev, *next;
do
{
if ( work->status == FREE )
{
work = work->next;
continue;
}
else
{
if ( --n == 0 )
{
printf("回收了位於%d的內存塊,其大小爲%dKB\n",work->addr,work->size);
prev = work->prev;
next = work->next;
occupied -= work->size;
if ( prev->status == FREE && next->status == FREE )
{
prev->size += work->size + next->size;
prev->next = next->next;
next->next->prev = prev;
if ( next == last )
last = prev;
free(work);
free(next);
}
else if ( prev->status == FREE && next->status == FULL )
{
prev->size += work->size;
prev->next = next;
next->prev = prev;
free(work);
}
else if ( prev->status == FULL && next->status == FREE )
{
work->size += next->size;
work->status = FREE;
work->next = next->next;
next->next->prev = work;
if ( next == last )
last = work;
free(next);
}
else
{
work->status = FREE;
}
--count;
return;
}
else
{
work = work->next;
}
}
}
while ( n > 0 );
}
}
作爲一個剛接觸內存管理的菜鳥,滋生如下問題,望牛人指教:
1. 我覺得在自己的項目裏添加一個內存模塊是不是整個項目的內存分配要合理一些(先malloc一塊大內存,再對其管理)?
2.內存分配會產生碎片,那對於碎片如何回收呢,如果我規定分配的內存塊大小都必須是2k的倍數(2k,4k,8k),對於每個請求的內存分配給它最相近的內存大小(如請求3k,分配4k的內存塊給它),產生的內存碎片是不是會少很多?
3.如果我的項目最多只需要分配幾個不同大小的內存塊(如3個12k,一個43k,2個6k),是不是先malloc(3*12+42+2*6)k內存,再分割成3個12k,一個43k,2個6k的內存塊,然後每次要分配的時候去FIT,是不是分配的效率會提升很多?
4.最後,也是最關鍵的一點,求內存管理方面的經典書籍?555。。。急求!!!