Malloc Lab
Malloc Lab Introduction
In this lab you will write a dynamic memory allocator which will consist of the malloc, free, realloc, and calloc functions. Your goal is to implement an allocator that is correct, efficient, and fast.
本lab的目的是用C语言编写一个正确高效和快速的动态存储分配器,即malloc,free,realloc,和calloc函数。主要是在mm.c这个文件中编写以下几个函数:
bool mm_init(void);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
void *calloc(size_t nmemb, size_t size);
bool mm_checkheap(int);
相关课件
- Lecture: Dynamic Memory Allocation - Basic Concepts
- Lecture: Dynamic Memory Allocation - Advanced Concepts
- Recitation: Malloc Lab Part 1
- Recitation: Malloc Lab Part 2
背景知识
使用动态内存申请器(比如malloc)是为了为那些在程序运行过程中才能确定大小的数据结构申请虚拟内存空间。
动态内存申请器管理的区域被称作堆,如下图。
- 动态内存申请器将堆这块区域当作一系列大小不同的块来管理,块或者式已经申请的,或者是空闲的。
- 动态内存申请器有两种:
- 显式申请器:应用负责申请和释放内存空间。如C语言中的malloc和free;
- 隐式申请器:应用申请内存空间,但是不释放内存空间。如Java中的new和垃圾回收机制(garbage collection)
相关函数
malloc:void* malloc(size_t size)
- 如果
size == 0
,返回NULL; - 成功:返回一个指针,该指针指向一块至少有
size
个字节的内存块。- 保证内存块的payload是16 bytes 对齐。
free:void free(void* bp)
- 释放指针
bp
指向的内存块; bp
必须是由malloc、calloc或realloc返回的指针;- 释放内存块之后需要和邻近的空闲块合并。
calloc:void *calloc(size_t elements, size_t size)
- 申请一段内存空间,并将这段内存空间初始化为0。
realloc:void *realloc(void *ptr, size_t size)
- 改变之前申请的一段内存块的大小。
内部碎片和外部碎片
-
内部碎片 (Internal Fragmentation)
Space within an allocated block that cannot be used for storing data, because it is required for some of the manager’s data structures (e.g., headers, footers, and free-list pointers).
- 当Payload大小(绿色部分)小于Block大小的时候就会出现内部碎片,图中两边的灰色部分就是内部碎片。灰色部分在进程占有这块存储块时无法被利用,直到进程释放它,或进程结束时,系统才有可能利用这个存储块。
-
外部碎片 (External Fragmentation)
Unused space between allocated blocks or at the end of the heap.
#define SIZ sizeof(size_t)
- 外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域,即处于任何两个已分配区域或页面之间的没有被使用的空闲存储块。这些存储块的总和可以满足当前申请的长度要求,但是由于它们的地址不连续或其他原因,使得系统无法满足当前申请。
吞吐量和利用率
吞吐量和利用率是这个lab考察分配器效率的两个因素:
- 吞吐量 throughput
- Number of completed requests per unit time 单位时间内完成的请求数 (kilo-operations per second or KOPS)
- 吞吐量衡量的是分配器的运行速度,换言之也就是在整个堆中查找空闲内存块的速度。查找空闲块的方法。
- 利用率 utilization
- The peak ratio between the aggregate amount of memory used by the driver (i.e., allocated via malloc but not yet freed via free) and the size of the heap used by your allocator.
- 利用率衡量的是分配器的内外部碎片,在编写过程中一定要减小内外部碎片的出现。
需要注意的是,吞吐量和利用率是一对相冲突的目标。吞吐量最大化的同时不能够让利用率最大化,反之也是。所以在提升利用率的同时需要保证相对快的运行速度。
查找空闲块
在查找空闲块的过程中,当有多个空闲块满足条件时,有几个不同的策略:
- First Fit:使用第一个找到的空闲块;
static block_t *first_fit(size_t asize) {
block_t *block;
for (block = heap_start;