动态内存分配的策略研究
使用Carnegie Mellon University提供的C语言模拟代码为测试,验证多种分配策略的特性,为设计新的更高效的算法进行探路,工程文件附于文章末尾。
基本宏定义
阅读C语言测试模型,得到基本分配块的结构约定。
#define ALIGNMENT 8 //校准字长,4或8
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)//字对齐,向上取整,保证容纳
#define SIZE_T_SIZE (ALIGN(sizeof(size_t)))
#define WSIZE 4 // 字和头部/脚部的大小(bytes)
#define DSIZE 8 // 双字
#define CHUNKSIZE (1 << 12) // 按照CHUNKSIZE大小(bytes)扩展堆
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#define HDRP(bp) ((char *)(bp)-WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp)-WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp)-GET_SIZE(((char *)(bp)-DSIZE)))
#define SEGREGATED//采用的策略
显式空闲链表
static void _insert(char* bp) {
// 指针先初始化为空
PUT(NEXT_LINK_RP(bp), 0);
PUT(PREV_LINK_RP(bp), 0);
#ifdef LIFO
char* t_head = GET(list_head);
if (t_head) { // 链表不为空
PUT(PREV_LINK_RP(t_head), bp);
}
PUT(NEXT_LINK_RP(bp), t_head);
PUT(list_head, bp);
#else
char* cur = GET(list_head);
if (!cur) { // 链表为空
PUT(NEXT_LINK_RP(bp), cur);
PUT(PREV_LINK_RP(bp), list_head);
PUT(list_head, bp);
return;
}
// 循环条件为下一个节点不为空,且当前节点地址小于bp
while (cur < bp && GET(NEXT_LINK_RP(cur))) {
cur = GET(NEXT_LINK_RP(cur));
}
if (cur >= bp) { // 要将bp节点插入到cur之前
char* prev = GET(PREV_LINK_RP(cur)); // 注意:PREV_LINK_RP获取的是指针的地址,GET后才是指针的值
PUT(NEXT_LINK_RP(bp), cur);
PUT(PREV_LINK_RP(bp), prev);
PUT(NEXT_LINK_RP(prev), bp);
PUT(PREV_LINK_RP(cur), bp);
}
else { // 没有后续节点了,尾插
PUT(NEXT_LINK_RP(cur), bp);
PUT(PREV_LINK_RP(bp), cur);
}
#endif
}
// 链表移除节点
static void _remove(char* bp) {
char* next = GET(NEXT_LINK_RP(bp));
char* prev = GET(PREV_LINK_RP(bp));
if (!prev) { // 头节点
if (next) PUT(PREV_LINK_RP(next), 0);
PUT(list_head, next);
}
else { // 非头
if (next) PUT(PREV_LINK_RP(next), prev);
PUT(NEXT_LINK_RP(prev), next);
}
}
#endif
// 私有全局变量及函数
static char* heap_listp;
static void* extend_heap(size_t words);
static void* coalesce(void* bp);
static void* find_fit(size_t asize);
static void place(void* bp, size_t asize);
static void* extend_heap(size_t words) {
char* bp;
size_t size;
// 为对齐,分配偶数个字
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
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)); // 新的结尾块
// 合并空闲块
return coalesce(bp);
}
static void* coalesce(void* bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); // 通过前一个块的脚部获取分配状态
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); // 通过后一个块的头部获取分配状态
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && !next_alloc) { // 下一个块为空闲
size += GET_SIZE(HDRP(NEXT_BLKP(bp))); // 加上下一个块的size
_remove(NEXT_BLKP(bp)); // 先从空闲链表删除下一个块
PUT(HDRP(bp), PACK(size, 0)); // 修改bp块的头部
PUT(FTRP(bp), PACK(size, 0)); // 修改“下一块”的脚部
// 需要注意的是:FTRP是通过HDRP运作的,所以要注意两者的先后关系
}
else if (!prev_alloc && next_alloc) { // 上一个块为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
_remove(PREV_BLKP(bp));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else if (!prev_alloc && !next_alloc) { // 上下两个块皆为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
_remove(PREV_BLKP(bp));
_remove(NEXT_BLKP(bp));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
_insert(bp); // 插入合并后的块,若不需要合并,直接插入
return bp;
}
static void* find_fit(size_t asize) {
void* bp = GET(list_head);
while (bp) {
if (GET_SIZE(HDRP(bp)) >= asize) return bp;
bp = GET(NEXT_LINK_RP(bp));
}
return NULL;
}
static void place(void* bp, size_t asize) {
size_t csize = GET_SIZE(HDRP(bp));
_remove(bp); // 先从空闲链表删除bp块
// 当分割剩下的块大小 >= 最小块大小(2*DSIZE)时才进程分割
if ((csize - asize) >= (2 * DSIZE)) {
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1)); // 需要注意的是:FTRP是通过HDRP运作的,所有要注意两者的先后关系
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize - asize, 0));
PUT(FTRP(bp), PACK(csize - asize, 0));
coalesce(bp); // 将多余的空闲块合并
}
else {
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
}
int mm_init(void) {
if ((heap_listp = mem_sbrk(6 * WSIZE)) == (void*)-1) return -1;
PUT(heap_listp, 0); // 第一个字是双字边界对齐的不使用填充字
// 哨兵节点
PUT(heap_listp + (1 * WSIZE), 0); // next
PUT(heap_listp + (2 * WSIZE), 0); // prev
// 序言块是一个8字节的已分配块,只由一个头部和脚部组成,序言块和结尾块允许我们忽略潜在的麻烦边界问题
PUT(heap_listp + (3 * WSIZE), PACK(DSIZE, 1)); // 序言块
PUT(heap_listp + (4 * WSIZE), PACK(DSIZE, 1)); // 序言块
PUT(heap_listp + (5 * WSIZE), PACK(0, 1)); // 结尾块
list_head = heap_listp + (1 * WSIZE);
heap_listp += (4 * WSIZE); // 指向序言快的下一个字节
// 以空闲块扩展空堆
if (extend_heap(CHUNKSIZE / WSIZE) == NULL) return -1;
return 0;
}
隐式链表
// 私有全局变量及函数
static char* heap_listp;
static void* extend_heap(size_t words);
static void* coalesce(void* bp);
static void* find_fit(size_t asize);
static void place(void* bp, size_t asize);
static void* extend_heap(size_t words) {
char* bp;
size_t size;
// 为对齐,分配偶数个字
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
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)); // 新的结尾块
// 合并空闲块
return coalesce(bp);
}
static void* coalesce(void* bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); // 通过前一个块的脚部获取分配状态
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); // 通过后一个块的头部获取分配状态
size_t size = GET_SIZE(HDRP(bp));
// 都已分配
if (prev_alloc && next_alloc) {
return bp;
}
else if (prev_alloc && !next_alloc) { // 下一个块为空闲
size += GET_SIZE(HDRP(NEXT_BLKP(bp))); // 加上下一个块的size
PUT(HDRP(bp), PACK(size, 0)); // 修改bp块的头部
PUT(FTRP(bp), PACK(size, 0)); // 修改“下一块”的脚部
// 需要注意的是:FTRP是通过HDRP运作的,所以要注意两者的先后关系
}
else if (!prev_alloc && next_alloc) { // 上一个块为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else { // 上下两个块皆为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
return bp;
}
static void* find_fit(size_t asize)
{
void* bp;
for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) {
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp)))) return bp;
}
return NULL;
}
static void place(void* bp, size_t asize) {
size_t csize = GET_SIZE(HDRP(bp));
// 当分割剩下的块大小 >= 最小块大小(2*DSIZE)时才进程分割
if ((csize - asize) >= (2 * DSIZE)) {
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1)); // 需要注意的是:FTRP是通过HDRP运作的,所有要注意两者的先后关系
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize - asize, 0));
PUT(FTRP(bp), PACK(csize - asize, 0));
coalesce(bp); // 将多余的空闲块合并
}
else {
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
}
int mm_init(void) {
if ((heap_listp = mem_sbrk(4 * WSIZE)) == (void*)-1) return -1;
PUT(heap_listp, 0); // 第一个字是双字边界对齐的不使用填充字
// 序言块是一个8字节的已分配块,只由一个头部和脚部组成,序言块和结尾块允许我们忽略潜在的麻烦边界问题
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 (extend_heap(CHUNKSIZE / WSIZE) == NULL) return -1;
return 0;
}
void* mm_malloc(size_t size) {
size_t asize; // 调整后的块的大小
size_t extendsize; // 扩展堆的大小
char* bp;
if (size == 0) return NULL;
// 调整块大小,包括头部和脚部的开销以及对齐要求
if (size <= DSIZE)
asize = 2 * DSIZE;
else // 向上取整到8的倍数,(DSIZE)是头部和脚部的开销
asize = DSIZE * ((size + (DSIZE)+(DSIZE - 1)) / DSIZE);
// 如果找到了合适的空闲块,分配
if ((bp = find_fit(asize)) != NULL) {
place(bp, asize);
return bp;
}
// 没找到,扩展堆,再分配
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL) return NULL;
place(bp, asize);
return bp;
}
void mm_free(void* bp) {
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp);
}
void* mm_realloc(void* ptr, size_t size) {
void* oldptr = ptr;
void* newptr;
size_t copySize;
// size==0,只需要进行释放
if (size == 0) {
mm_free(oldptr);
return NULL;
}
// 如果ptr为空,只需要调用mm_malloc即可
if (oldptr == NULL) {
return mm_malloc(size);
}
newptr = mm_malloc(size);
if (newptr == NULL) return NULL;
size = GET_SIZE(HDRP(oldptr)); // 获取原块的大小
copySize = GET_SIZE(HDRP(newptr)); // 获取新块的大小
if (size < copySize) copySize = size; // 截断
memcpy(newptr, oldptr, copySize - DSIZE); //-DSIZE 是减去头部脚部的开销
mm_free(oldptr);
return newptr;
}
分离适配
#define NEXT_LINK_RP(bp) ((char *)(bp))
#define PREV_LINK_RP(bp) ((char *)(bp) + WSIZE)
static char* heap_listp;
static char* block_list_start;
static void* extend_heap(size_t words);
static void* coalesce(void* bp);
static void* find_fit(size_t asize);
static void* place(void* bp, size_t asize);
static void _remove(char* bp);
static void _insert(char* bp);
static char* find_list(size_t size); /** 查找该大小所在的链表 **/
#define SIZE_0 16
#define SIZE_1 32
#define SIZE_2 64
#define SIZE_3 128
#define SIZE_4 256
#define SIZE_5 512
#define SIZE_6 1024
#define SIZE_7 2048
#define SIZE_8 4096
#define SIZE_9 8192
// 共有11个大小类,最后一个是>8192的统一划分
#define SIZE_CLASS_CNT 11
int mm_init(void) {
if ((heap_listp = mem_sbrk(14 * WSIZE)) == (void*)-1) return -1;
// PUT(heap_listp, 0); // 第一个字是双字边界对齐的不使用填充字
// 大小类的块大小划分最多到2^13=8192,因为测试文件测试的大小大多不超过8192,除了一些特殊的测试文件,统一划分到一个链表
// 2^4 2^5 2^6 ... 2^13 >8192,由于空闲块的设计以及对齐要求,最小的块要求为16字节,所以从2^4次方开始
for (int i = 0; i < 11; ++i) {
PUT(heap_listp + (i * WSIZE), 0);
}
PUT(heap_listp + (11 * WSIZE), PACK(DSIZE, 1)); // 序言头
PUT(heap_listp + (12 * WSIZE), PACK(DSIZE, 1)); // 序言尾
PUT(heap_listp + (13 * WSIZE), PACK(0, 1)); // 结尾块
block_list_start = heap_listp;
heap_listp += (12 * WSIZE);
if (extend_heap((CHUNKSIZE + 8) / WSIZE) == NULL) return -1;
return 0;
}
void* mm_malloc(size_t size) {
size_t asize; // 调整后的块的大小
size_t extendsize; // 扩展堆的大小
char* bp;
if (size == 0) return NULL;
// 调整块大小,包括头部和脚部的开销以及对齐要求
if (size <= DSIZE)
asize = 2 * DSIZE;
else // 向上取整到8的倍数,(DSIZE)是头部和脚部的开销
asize = DSIZE * ((size + (DSIZE)+(DSIZE - 1)) / DSIZE);
// 如果找到了合适的空闲块,分配
if ((bp = find_fit(asize)) != NULL) {
return place(bp, asize);
}
// 没找到,扩展堆,再分配
extendsize = MAX(asize, CHUNKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL) return NULL;
return place(bp, asize);
}
void mm_free(void* bp) {
size_t size = GET_SIZE(HDRP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp);
}
void* mm_realloc(void* ptr, size_t size) {
void* newptr = ptr;
size_t copySize, asize, total_size;
// size==0,只需要进行释放
if (size == 0) {
mm_free(ptr);
return NULL;
}
// 如果ptr为空,只需要调用mm_malloc即可
if (ptr == NULL) {
return mm_malloc(size);
}
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (DSIZE)+(DSIZE + 1)) / DSIZE);
size_t old_size = GET_SIZE(HDRP(ptr));
size_t next_size = GET_SIZE(HDRP(NEXT_BLKP(ptr)));
size_t prev_size = GET_SIZE(HDRP(PREV_BLKP(ptr)));
// 原先size大小满足要求,直接返回原指针
if (old_size >= asize)
return ptr;
else if (!GET_ALLOC(HDRP(NEXT_BLKP(ptr))) && old_size + next_size >= asize) {
total_size = old_size + next_size;
_remove(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(total_size, 1));
PUT(FTRP(ptr), PACK(total_size, 1));
}
else if (!next_size) {
if (extend_heap(MAX(asize - old_size, CHUNKSIZE)) == NULL) return NULL;
total_size = old_size + GET_SIZE(HDRP(NEXT_BLKP(ptr)));
_remove(NEXT_BLKP(ptr));
PUT(HDRP(ptr), PACK(total_size, 1));
PUT(FTRP(ptr), PACK(total_size, 1));
}
else if (!GET_ALLOC(HDRP(PREV_BLKP(ptr))) && old_size + prev_size >= asize) {
total_size = old_size + prev_size;
_remove(PREV_BLKP(ptr));
PUT(FTRP(ptr), PACK(total_size, 1));
PUT(HDRP(PREV_BLKP(ptr)), PACK(total_size, 1));
newptr = PREV_BLKP(ptr);
memmove(newptr, ptr, old_size); // 有可能出现重叠区域要用memmove来代替memcpy
}
else {
newptr = mm_malloc(asize);
memcpy(newptr, ptr, old_size - DSIZE); //-DSIZE 是减去头部脚部的开销
mm_free(ptr);
}
return newptr;
}
static void* extend_heap(size_t words) {
char* bp;
size_t size;
// 为对齐,分配偶数个字
size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
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)); // 新的结尾块
// 合并空闲块
return coalesce(bp);
}
static void* coalesce(void* bp) {
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); // 通过前一个块的脚部获取分配状态
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); // 通过后一个块的头部获取分配状态
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && !next_alloc) { // 下一个块为空闲
size += GET_SIZE(HDRP(NEXT_BLKP(bp))); // 加上下一个块的size
_remove(NEXT_BLKP(bp)); // 先从空闲链表删除下一个块
PUT(HDRP(bp), PACK(size, 0)); // 修改bp块的头部
PUT(FTRP(bp), PACK(size, 0)); // 修改“下一块”的脚部
// 需要注意的是:FTRP是通过HDRP运作的,所以要注意两者的先后关系
}
else if (!prev_alloc && next_alloc) { // 上一个块为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
_remove(PREV_BLKP(bp));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
else if (!prev_alloc && !next_alloc) { // 上下两个块皆为空闲
size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp)));
_remove(PREV_BLKP(bp));
_remove(NEXT_BLKP(bp));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
bp = PREV_BLKP(bp);
}
_insert(bp); // 插入合并后的块,若不需要合并,直接插入
return bp;
}
static void* find_fit(size_t asize) {
char* head = find_list(asize);
// 如果在head的空闲链表找不到,就搜索下一个更大的大小类的空闲链表
for (; head < block_list_start + (SIZE_CLASS_CNT * WSIZE); head += WSIZE) {
char* bp = GET(head);
while (bp) {
if (GET_SIZE(HDRP(bp)) >= asize) return bp;
bp = GET(NEXT_LINK_RP(bp));
}
}
return NULL;
}
static void* place(void* bp, size_t asize) {
void* res_bp = bp;
size_t csize = GET_SIZE(HDRP(bp));
size_t remain_size = csize - asize;
_remove(bp); // 先从空闲链表删除bp块
if (remain_size < (2 * DSIZE)) {
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
}
else if (asize < 88) {
PUT(HDRP(bp), PACK(remain_size, 0));
PUT(FTRP(bp), PACK(remain_size, 0));
_insert(bp);
bp = NEXT_BLKP(bp);
res_bp = bp;
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
}
else {
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1)); // 需要注意的是:FTRP是通过HDRP运作的,所有要注意两者的先后关系
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(remain_size, 0));
PUT(FTRP(bp), PACK(remain_size, 0));
_insert(bp);
}
return res_bp;
}
// debug辅助函数,检查链表是否按照大小排序,且返回链表节点个数
static int check_list(char* bp) {
int size = 0;
int cnt = 0;
while (bp && GET_SIZE(HDRP(bp)) >= size) {
size = GET_SIZE(HDRP(bp));
bp = GET(NEXT_LINK_RP(bp));
++cnt;
}
return cnt;
}
// 链表插入
static void _insert(char* bp) {
// 指针先初始化为空
PUT(NEXT_LINK_RP(bp), 0);
PUT(PREV_LINK_RP(bp), 0);
#ifdef LIFO
char* head_ptr = find_list(GET_SIZE(HDRP(bp))); // 头节点指针的地址
char* head = GET(head_ptr); // 头节点地址
if (head) {
PUT(PREV_LINK_RP(head), bp);
}
PUT(NEXT_LINK_RP(bp), head);
PUT(PREV_LINK_RP(bp), head_ptr);
PUT(head_ptr, bp);
#else
int bp_size = GET_SIZE(HDRP(bp));
char* head_ptr = find_list(bp_size);
char* cur = GET(head_ptr);
if (!cur) {
PUT(NEXT_LINK_RP(bp), cur);
PUT(PREV_LINK_RP(bp), head_ptr);
PUT(head_ptr, bp);
return;
}
while (GET_SIZE(HDRP(cur)) < bp_size && GET(NEXT_LINK_RP(cur))) {
cur = GET(NEXT_LINK_RP(cur));
}
if (GET_SIZE(HDRP(cur)) >= bp_size) {
char* prev = GET(PREV_LINK_RP(cur));
PUT(NEXT_LINK_RP(bp), cur);
PUT(PREV_LINK_RP(bp), prev);
PUT(NEXT_LINK_RP(prev), bp);
PUT(PREV_LINK_RP(cur), bp);
}
else {
PUT(NEXT_LINK_RP(cur), bp);
PUT(PREV_LINK_RP(bp), cur);
}
#endif
}
// 链表移除节点
static void _remove(char* bp) {
char* head_ptr = find_list(GET_SIZE(HDRP(bp))); // 地址
char* head = GET(head_ptr); // 值
char* next = GET(NEXT_LINK_RP(bp));
char* prev = GET(PREV_LINK_RP(bp));
if (prev == head_ptr) { // 头节点
if (next) {
// dummy -> bp -> xxx
PUT(PREV_LINK_RP(next), prev);
PUT(head_ptr, next);
}
else {
// dummy ->bp
PUT(head_ptr, NULL);
}
}
else { // 非头
if (next) {
// dummy -> xxx -> bp -> xxx
PUT(PREV_LINK_RP(next), prev);
PUT(NEXT_LINK_RP(prev), next);
}
else {
// dummy -> xxx -> bp
PUT(NEXT_LINK_RP(prev), NULL);
}
}
}
static char* find_list(size_t size) {
int i = 0;
if (size <= SIZE_0)
i = 0;
else if (size <= SIZE_1)
i = 1;
else if (size <= SIZE_2)
i = 2;
else if (size <= SIZE_3)
i = 3;
else if (size <= SIZE_4)
i = 4;
else if (size <= SIZE_5)
i = 5;
else if (size <= SIZE_6)
i = 6;
else if (size <= SIZE_7)
i = 7;
else if (size <= SIZE_8)
i = 8;
else if (size <= SIZE_9)
i = 9;
else
i = 10;
return block_list_start + (i * WSIZE);
}
#endif
显然,分离适配,即所谓的‘桶’式数据结构效率最高。在此基础上,可以测试伙伴系统等策略,这里不再赘述。
工程文件如下:https://files.cnblogs.com/files/blogs/824579/malloc.tar.gz?t=1722038080&download=true
本文作者:邓佑孤
本文链接:https://www.cnblogs.com/Arc-ux/p/18323348
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步