文章目录
实验目的
本次实验的核心目标是通过亲手实现一个动态内存分配器,深入理解内存管理的机制,并掌握动态内存分配的关键技术。在计算机科学领域,动态内存分配是程序设计中的一个基础而关键的技能,它直接关系到程序的性能和资源的有效利用。本实验旨在达到以下几个目的:
- 实现内存分配器的基本概念:通过编写自定义版本的
mm_malloc
、mm_free
和mm_realloc
函数,我们能够详细了解内存分配、释放和重新分配的过程。 - 应用内存对齐和边界标记:在实现中,我们采用了内存对齐技术,确保所有分配的内存块都是8字节对齐的,并使用了边界标记来维护内存块信息,以便于内存管理。
- 探索和实现内存分配策略:我们采用了分离链表的策略,通过维护不同大小类别的空闲块链表来优化内存分配和释放的效率。此外,我们还实现了内存块的合并,以减少内存碎片。
- 提升问题解决能力:在实现过程中,我们面临了多种挑战,如内存泄漏、内存碎片等,通过解决这些问题,我们提升了我们的问题解决技巧。
- 培养严谨的编程习惯:实验要求我们编写清晰、高效且易于维护的代码,这有助于我们在未来的研究或工作中形成良好的编程习惯。
- 性能测试与优化:我们使用
mdriver
测试工具对内存分配器进行了性能测试,并根据测试结果对内存分配策略进行了优化。
实验思路
本实验的核心目标是实现一个高效且内存利用率高的动态内存分配器。为了实现这一目标,我们采用了以下策略:
内存分配器初始化 (mm_init
)
在初始化阶段,我们首先创建了一个初始的空堆,用于存储后续分配的内存块。同时,我们为每个可能的内存块大小类别初始化了一个空闲链表,并将这些链表的头指针存储在一个数组中。这一步骤是后续内存分配和管理的基础。
内存分配 (mm_malloc
)
mm_malloc
函数的实现是整个内存分配器的核心。我们首先根据请求的内存大小计算出合适的块大小,并在对应的空闲链表中搜索合适的内存块。如果当前链表中没有合适的块,则通过扩展堆来获取新的内存块。在分配内存块时,我们采用了首次适应算法,即从空闲链表的头部开始搜索,直到找到足够大的空闲块。
内存对齐处理
为了确保内存访问的效率,我们对请求的内存大小进行了对齐处理,保证了所有分配的内存块都是8字节对齐的。
空闲链表搜索
我们遍历了从最小到最大的空闲链表,寻找第一个足够大的空闲块。这一策略旨在减少内存碎片,并提高内存利用率。
内存块的分割与合并
当找到的空闲块大于请求的内存大小时,我们将其分割成两部分:一部分用于满足当前的内存请求,另一部分放回空闲链表中,以供后续的内存请求使用。
内存释放 (mm_free
)
在 mm_free
函数中,我们首先将内存块标记为空闲状态,然后尝试与相邻的空闲块合并,以减少内存碎片。这一步骤通过检查内存块的前后邻居是否也为空闲状态来实现。
空闲链表的维护
将空闲的内存块重新插入到对应的空闲链表中时,我们根据其大小选择正确的链表,以保持链表的有序性。
内存重新分配 (mm_realloc
)
mm_realloc
函数用于调整已分配内存块的大小。我们首先检查新请求的大小与原内存块的大小关系,如果原内存块足够大,则无需进行内存移动;否则,我们将分配一个新的内存块,复制数据,并释放旧的内存块。
内存块的扩展与收缩
在调整内存块大小时,我们考虑了内存块的扩展和收缩两种情况,以满足不同的内存需求。
内存复制与释放
在必要时,我们使用了内存复制函数来迁移数据,并释放了旧的内存块,以避免数据丢失。
完整代码
以下是本实验中关键函数的实现细节:
/*
* mm-naive.c - The fastest, least memory-efficient malloc package.
*
* In this naive approach, a block is allocated by simply incrementing
* the brk pointer. A block is pure payload. There are no headers or
* footers. Blocks are never coalesced or reused. Realloc is
* implemented directly using mm_malloc and mm_free.
*
* NOTE TO STUDENTS: Replace this header comment with your own header
* comment that gives a high level description of your solution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include "mm.h"
#include "memlib.h"
/*********************************************************
* NOTE TO STUDENTS: Before you do anything else, please
* provide your team information in the following struct.
********************************************************/
team_t team = {
/* Team name */
"逆天滔博滚回去调整",
/* First member's full name */
"=_=",
/* First member's email address */
"",
/* Second member's full name (leave blank if none) */
"",
/* Second member's email address (leave blank if none) */
""
};
#define ALIGNMENT 8
/* 向8取整*/
#define ALIGN(size)(((size) + (ALIGNMENT-1)) & ~0x7)
#define WSIZE 8 /* 字的大小 */
#define DSIZE 16 /* 双字的大小*/
#define CHUNKSIZE (1<<13) /* 扩展堆的字节大小 */
#define INIT_SIZE (1<<6) /* 初始化堆的字节大小 */
#define FIXED_SIZE 16 /* 头部和脚部的结点大小 */
#define MAX_SIZE 16 /*定义空闲链表的大小*/
#define MINBLOCKSIZE 32//最小块的大小
#define MAX(x, y) ((x) > (y) ? (x) : (y))/*x、y中最大的数*/
#define PACK(size, alloc) ((size) | (alloc))//将size和alloc打包
#define GET(p) (*(size_t *)(p))//获得p处的值
#define PUT(p, val) (*(size_t *)(p) = (val))//将p处赋值val
#define GET_SIZE(p) (GET(p) & ~0x7)//得到大小
#define GET_ALLOC(p) (GET(p) & 0x1)//得到分配位
#define HDRP(p) ((char *)(p) - WSIZE)//得到头指针
#define FTRP(p) ((char *)(p) + GET_SIZE(HDRP(p)) - DSIZE)//得到尾指针
#define NEXT_BLKP(p) ((char *)(p) + GET_SIZE(((char *)(p) - WSIZE)))//得到下一个块的地址
#define PREV_BLKP(p) ((char *)(p) - GET_SIZE(((char *)(p) - DSIZE)))//得到上一个块的地址
#define PREV_FREE_PTR(p) ((char *)(p))//得到链表中的上一块的地址
#define PREV_FREE(p) (*(char **)(p))
#define NEXT_FREE_PTR(p) ((char *)(p) + WSIZE)//得到下一块的地址
#define NEXT_FREE(p) (*(char **)(((char *)(p) + WSIZE)))
#define SET_P(p, new_next) (*(size_t *)(p) = (size_t)(new_next))//给p处指针赋值
static char *heap_listp;
void *headlist[MAX_SIZE];
void *exten_hp(size_t size);
void *merge_block(void *p);
void *set_block(void *p, size_t size);
//static void printblock(void *p);
//static void checkblock(void *p);
void insert_block(void *p, size_t size);
void delete_block(void *p);
int mm_init(void){
for(int j=0; j<MAX_SIZE; j++)/*初始化空闲链表*/
headlist[j] = NULL;
heap_listp = mem_sbrk(4*WSIZE);
if (heap_listp == NULL)
return -1;
PUT(heap_listp, 0);//填充字
PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1));//序言块头部
PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1));//序言块尾部
PUT(heap_listp + (3
* WSIZE), PACK(0, 1));//结尾块
heap_listp += (2 * WSIZE);//移动指针
if (exten_hp(INIT_SIZE) == NULL)//扩展堆
return -1;
return 0;
}
void *mm_malloc(size_t size){
size_t adjust_size;
size_t extend_size;
char *p;
if (size == 0)
return NULL;
if (size <= DSIZE)/*需要分配的块的大小*/
adjust_size = MINBLOCKSIZE;
else
adjust_size = ALIGN(size + FIXED_SIZE);
for(int j=0; j<MAX_SIZE; j++)/*寻找大小最合适的空闲链表*/
{
if((adjust_size <= (1<<(j+5)))&&(headlist[j]!=NULL))
{
for(p = headlist[j]; p != NULL; p = NEXT_FREE(p))/*寻找大小最合适的块*/
{
if(adjust_size <= GET_SIZE(HDRP(p)))
{
p = set_block(p, adjust_size);/*将其分配出去*/
return p;
}
}
}
}
extend_size = MAX(adjust_size, CHUNKSIZE);/*没有找到则拓展堆*/
if ((p = exten_hp(extend_size)) == NULL)
return NULL;
p = set_block(p, adjust_size);
return p;
}
void mm_free(void *bp)
{
if(bp==NULL) return ;
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
insert_block(bp, size);//插入空闲链表
merge_block(bp);
}
void *mm_realloc(void *bp, size_t size){
if((int)size<0) return NULL;
if((int)size==0)
{
mm_free(bp);
return NULL;
}
size_t old_size = GET_SIZE(HDRP(bp));//得到老的块的大小
size_t new_size = size<=DSIZE?MINBLOCKSIZE:ALIGN(size+FIXED_SIZE);
if(new_size == old_size) return bp;//新旧块一样大
if(new_size <= old_size)
{
size = old_size - new_size;
if(size >= MINBLOCKSIZE)
{
PUT(HDRP(bp), PACK(new_size, 1));
PUT(FTRP(bp), PACK(new_size, 1));
void *p = NEXT_BLKP(bp);
PUT(HDRP(p), PACK(size, 0));
PUT(FTRP(p), PACK(size, 0));
insert_block(p, size);
}
return bp;
}
void *next_block = NEXT_BLKP(bp);
if(!GET_ALLOC(HDRP(next_block))&&(GET_SIZE(HDRP(next_block))+old_size>=new_size))/*如果前后合并后满足*/
{
delete_block(next_block);
size = GET_SIZE(HDRP(next_block)) + old_size - new_size;
if(size>=MINBLOCKSIZE)
{
PUT(HDRP(bp), PACK(new_size, 1));
PUT(FTRP(bp), PACK(new_size, 1));
void *p = NEXT_BLKP(bp);
PUT(HDRP(p), PACK(size, 0));
PUT(FTRP(p), PACK(size, 0));
insert_block(p, size);
}
else
{
PUT(HDRP(bp), PACK(old_size+GET_SIZE(HDRP(next_block)), 1));
PUT(FTRP(bp), PACK(old_size+GET_SIZE(HDRP(next_block)), 1));
}
return bp;
}
else
{
void *new_ptr = mm_malloc(new_size-FIXED_SIZE);
if (new_ptr == NULL) return NULL;
memcpy(new_ptr, bp, old_size-FIXED_SIZE);
mm_free(bp);
return new_ptr;
}
}
void *exten_hp(size_t size){
char *bp;
size = ALIGN(size);
if ((long)(bp = mem_sbrk(size)) == -1)
return NULL;
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));
insert_block(bp, size);
return merge_block(bp);
}
void insert_block(void *bp, size_t size){/*空闲链表的插入*/
int j = 0;
void *p = NULL;
void *next = NULL;
while ((j < MAX_SIZE - 1) && (size > 1))/*找到插入的地方*/
{
size >>= 1;
j++;
}
p = headlist[j];
while ((p != NULL) && (size > GET_SIZE(HDRP(p))))
{
next = p;
p = NEXT_FREE(p);
}
if (p != NULL)
{
if (next != NULL)
{
SET_P(NEXT_FREE_PTR(bp), p);
SET_P(PREV_FREE_PTR(p), bp);
SET_P(PREV_FREE_PTR(bp), next);
SET_P(NEXT_FREE_PTR(next), bp);
}
else
{
SET_P(NEXT_FREE_PTR(bp), p);
SET_P(PREV_FREE_PTR(p), bp);
SET_P(PREV_FREE_PTR(bp), NULL);
headlist[j] = bp;
}
}
else
{
if (next != NULL)
{
SET_P(NEXT_FREE_PTR(bp), NULL);
SET_P(PREV_FREE_PTR(bp), next);
SET_P(NEXT_FREE_PTR(next), bp);
}
else
{
SET_P(NEXT_FREE_PTR(bp), NULL);
SET_P(PREV_FREE_PTR(bp), NULL);
headlist[j] = bp;
}
}
}
void delete_block(void *bp){/*空闲链表的删除*/
int j = 0;
size_t size = GET_SIZE(HDRP(bp));
while ((j < MAX_SIZE - 1) && (size > 1))
{
size >>= 1;
j++;
}
if (NEXT_FREE(bp) != NULL)
{
if (PREV_FREE(bp) != NULL)
{
SET_P(PREV_FREE_PTR(NEXT_FREE(bp)), PREV_FREE(bp));
SET_P(NEXT_FREE_PTR(PREV_FREE(bp)), NEXT_FREE(bp));
}
else
{
SET_P(PREV_FREE_PTR(NEXT_FREE(bp)), NULL);
headlist[j] = NEXT_FREE(bp);
}
}
else
{
if (PREV_FREE(bp) != NULL)
{
SET_P(NEXT_FREE_PTR(PREV_FREE(bp)), NULL);
}
else
{
headlist[j] = NULL;
}
}
}
void *merge_block(void *bp){/*合并块*/
size_t prev = GET_ALLOC(FTRP(PREV_BLKP(bp)));
size_t next = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev && next)
{
return bp;
}
else if (prev && !next)
{
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
delete_block(NEXT_BLKP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
else if (!prev && next)
{
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
bp = PREV_BLKP(bp);
delete_block(bp);
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
else
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
delete_block(PREV_BLKP(bp));
delete_block(NEXT_BLKP(bp));
bp = PREV_BLKP(bp);
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
insert_block(bp, size);
return bp;
}
void *set_block(void *bp, size_t adjust_size){/*分割块*/
size_t csize = GET_SIZE(HDRP(bp));
delete_block(bp);
if ((csize - adjust_size) >= MINBLOCKSIZE)
{
PUT(HDRP(bp), PACK(adjust_size, 1));
PUT(FTRP(bp), PACK(adjust_size, 1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize - adjust_size, 0));
PUT(FTRP(bp), PACK(csize - adjust_size, 0));
insert_block(bp, csize - adjust_size);
}
else
{
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
return bp;
}