[C语言实战]C语言内存管理实战:实现自定义malloc与free(四)

[C语言实战]C语言内存管理实战:实现自定义malloc与free(四)

摘要:通过实现简化版的内存管理器,深入理解动态内存分配的核心原理。本文包含内存块设计、分配算法、空闲合并策略的完整实现,并附可运行的代码和测试用例。

一、动态内存管理原理剖析

1.1 内存分配的核心需求

  • 动态分配:程序运行时按需请求内存
  • 碎片管理:减少内部碎片(分配过大)和外部碎片(空闲块分散)
  • 效率平衡:时间(搜索速度)与空间(利用率)的权衡

1.2 内存块结构设计

typedef struct mem_block {size_t size;         // 可用内存大小(不含头部)int free;            // 空闲标记(1=空闲,0=占用)struct mem_block *next;  // 链表指针
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block)  // 头部元数据大小

内存布局

[ Header | Allocated Memory ] -> [ Header | Allocated Memory ] -> ...

1.3 分配策略对比

策略优点缺点
首次适应搜索速度快容易产生外部碎片
最佳适应内存利用率高搜索成本高
最差适应减少外部碎片大块请求可能失败

二、自定义malloc/free实现代码(test_alloc.c)

2.1 内存池初始化

// 内存块结构体定义(必须在使用前声明)
typedef struct mem_block {size_t size;          // 可用内存大小(不含头部)int free;             // 空闲标记(1=空闲,0=占用)struct mem_block *next; // 链表指针
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block) // 头部元数据大小
#define HEAP_SIZE (1024*1024)  // 1MB堆空间static char memory_pool[HEAP_SIZE];
static mem_block *free_list = NULL;void init_memory_pool() {free_list = (mem_block*)memory_pool;free_list->size = HEAP_SIZE - BLOCK_HEADER_SIZE;free_list->free = 1;free_list->next = NULL;
}

2.2 malloc实现(首次适应算法)

void* my_malloc(size_t size) {if (!free_list) init_memory_pool();  // 首次调用初始化mem_block *curr = free_list;while (curr) {if (curr->free && curr->size >= size) {// 切割内存块(剩余空间至少能存放头部)if (curr->size > size + BLOCK_HEADER_SIZE) {mem_block *new_block = (mem_block*)((char*)curr + BLOCK_HEADER_SIZE + size);new_block->size = curr->size - size - BLOCK_HEADER_SIZE;new_block->free = 1;new_block->next = curr->next;curr->size = size;curr->next = new_block;}curr->free = 0;return (void*)((char*)curr + BLOCK_HEADER_SIZE);  // 返回用户空间指针}curr = curr->next;}return NULL;  // 内存不足
}

2.3 free实现(相邻块合并)

void my_free(void *ptr) {if (!ptr) return;mem_block *block = (mem_block*)((char*)ptr - BLOCK_HEADER_SIZE);block->free = 1;// 前向合并mem_block *curr = free_list;while (curr) {if ((char*)curr + BLOCK_HEADER_SIZE + curr->size == (char*)block) {curr->size += BLOCK_HEADER_SIZE + block->size;curr->next = block->next;block = curr;}curr = curr->next;}// 后向合并if (block->next && block->next->free) {block->size += BLOCK_HEADER_SIZE + block->next->size;block->next = block->next->next;}
}

2.4 test_alloc.c完整代码

#include <stddef.h>   // 解决NULL和size_t未定义问题
#include <stdio.h>    // 用于printf调试输出
#include <assert.h>   // 用于断言测试// 内存块结构体定义(必须在使用前声明)
typedef struct mem_block {size_t size;          // 可用内存大小(不含头部)int free;             // 空闲标记(1=空闲,0=占用)struct mem_block *next; // 链表指针
} mem_block;#define BLOCK_HEADER_SIZE sizeof(mem_block) // 头部元数据大小
#define HEAP_SIZE (1024*1024)  // 1MB堆空间static char memory_pool[HEAP_SIZE];
static mem_block *free_list = NULL;void init_memory_pool() {free_list = (mem_block*)memory_pool;free_list->size = HEAP_SIZE - BLOCK_HEADER_SIZE;free_list->free = 1;free_list->next = NULL;
}void* my_malloc(size_t size) {if (!free_list) init_memory_pool();  // 首次调用初始化mem_block *curr = free_list;while (curr) {if (curr->free && curr->size >= size) {// 切割内存块(剩余空间至少能存放头部)if (curr->size > size + BLOCK_HEADER_SIZE) {mem_block *new_block = (mem_block*)((char*)curr + BLOCK_HEADER_SIZE + size);new_block->size = curr->size - size - BLOCK_HEADER_SIZE;new_block->free = 1;new_block->next = curr->next;curr->size = size;curr->next = new_block;}curr->free = 0;return (void*)((char*)curr + BLOCK_HEADER_SIZE);  // 返回用户空间指针}curr = curr->next;}return NULL;  // 内存不足
}void my_free(void *ptr) {if (!ptr) return;mem_block *block = (mem_block*)((char*)ptr - BLOCK_HEADER_SIZE);block->free = 1;// 前向合并mem_block *curr = free_list;while (curr) {if ((char*)curr + BLOCK_HEADER_SIZE + curr->size == (char*)block) {curr->size += BLOCK_HEADER_SIZE + block->size;curr->next = block->next;block = curr;}curr = curr->next;}// 后向合并if (block->next && block->next->free) {block->size += BLOCK_HEADER_SIZE + block->next->size;block->next = block->next->next;}
}// 内存状态打印函数
void print_memory_map() {mem_block *curr = free_list;printf("Memory Map:\n");while (curr) {printf("[%s | Size:%6zu] -> ", curr->free ? "FREE " : "USED ", curr->size);curr = curr->next;}printf("NULL\n");
}/****************** 测试代码 ******************/
int main() {// 基础功能测试printf("=== 基础分配测试 ===\n");int *arr = (int*)my_malloc(10*sizeof(int));assert(arr != NULL);printf("分配成功,地址:%p\n", arr);print_memory_map();my_free(arr);printf("释放后内存状态:\n");print_memory_map();return 0;
}

三、验证步骤

3.1 测试环境搭建

# 编译测试程序(保存为 test_alloc.c)
gcc -o test_alloc test_alloc.c -Wall -Wextra
# 运行测试程序
./test_alloc

3.2 预期结果

在这里插入图片描述

四、标准库malloc/free实现原理对比

4.1 glibc的ptmalloc核心设计

/* glibc的malloc_chunk结构(简化版)*/
struct malloc_chunk {size_t      prev_size;  /* 前块大小(当空闲时有效)*/size_t      size;       /* 块大小及标志位 */struct malloc_chunk* fd; /* 空闲链表的向前指针 */struct malloc_chunk* bk; /* 空闲链表的向后指针 */
};
核心机制对比表
特性自定义实现glibc ptmalloc
内存来源静态数组brk()/mmap()系统调用
分配粒度按需切割16字节对齐
空闲管理单向链表bins数组(fastbin/smallbin等)
线程安全使用arena锁
大内存处理不支持使用mmap独立映射
碎片处理相邻合并top chunk回收机制

4.2 标准库内存分配流程

malloc调用
请求大小<=128KB?
在arena中查找fastbin/smallbin
使用mmap直接分配
找到合适块?
拆分并返回内存
扩展top chunk
调用brk调整堆顶
创建独立内存映射

五、标准库高级特性实现

5.1 内存对齐分配

// glibc的memalign实现原理
void* glibc_memalign(size_t alignment, size_t size) {// 1. 分配额外空间用于对齐调整void* raw_ptr = malloc(size + alignment - 1);// 2. 计算对齐地址uintptr_t addr = (uintptr_t)raw_ptr;uintptr_t aligned_addr = (addr + alignment - 1) & ~(alignment - 1);// 3. 存储原始指针用于free((void**)aligned_addr)[-1] = raw_ptr;return (void*)aligned_addr;
}

5.2 内存池优化技术

/* glibc的tcache(线程缓存)结构 */
struct tcache_entry {struct tcache_entry *next;
};struct tcache_perthread_struct {char counts[TCACHE_MAX_BINS];  // 每个bin的计数struct tcache_entry *entries[TCACHE_MAX_BINS];
};
tcache工作流程:
  1. 每个线程维护独立缓存
  2. 小对象优先从tcache分配
  3. 释放时先返回tcache
  4. tcache满时退回全局arena

六、混合使用建议

6.1 何时使用自定义实现

实时系统
已知分配模式
理解原理
需要确定性行为
自定义
内存受限环境
教学/调试

6.2 标准库最佳实践

// 示例:使用malloc_trim主动归还内存
#include <malloc.h>void critical_memory_task() {// 执行前清理内存malloc_trim(0);// 执行关键任务// ...// 任务完成后再次清理malloc_trim(0);
}

七、扩展实验建议

7.1 Hook标准库函数

// 使用dlsym拦截malloc调用
#include <dlfcn.h>static void* (*real_malloc)(size_t) = NULL;void* malloc(size_t size) {if (!real_malloc) real_malloc = dlsym(RTLD_NEXT, "malloc");printf("申请 %zu 字节内存\n", size);return real_malloc(size);
}

编译命令:gcc -shared -ldl -fPIC -o libmymalloc.so myhook.c

7.2 内存泄漏检测

#define _GNU_SOURCE
#include <dlfcn.h>struct alloc_record {void* ptr;size_t size;const char* file;int line;
};static struct alloc_record allocs[MAX_RECORDS];void* dbg_malloc(size_t s, const char* file, int line) {void *p = malloc(s);add_record(p, s, file, line);return p;
}void dbg_free(void *p) {remove_record(p);free(p);
}// 使用宏覆盖标准函数
#define malloc(s) dbg_malloc(s, __FILE__, __LINE__)
#define free(p) dbg_free(p)

八、标准库实现演进

8.1 历史版本对比

版本特性改进点
glibc 2.3引入ptmalloc2多arena支持
glibc 2.10增加tcache线程局部缓存提升性能
glibc 2.26移除malloc hooks增强安全性
glibc 2.32新增malloc_info导出XML格式信息方便内存分析工具集成

8.2 第三方实现对比

// jemalloc的分配器注册(示例)
#include <jemalloc/jemalloc.h>int main() {// 显式使用jemallocvoid *p = je_malloc(1024);je_free(p);
}

8.3 主流分配器对比

特性ptmallocjemalloctcmalloc
设计目标通用型低碎片多线程优化
线程缓存tcache自动管理thread cache
大内存处理mmapextent中央堆
适用场景通用服务器内存敏感型应用高并发Web服务

希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84444.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv8源码修改(5)- YOLO知识蒸馏(下)设置蒸馏超参数:以yolov8-pose为例

目录 前言 1. 不同蒸馏算法资源占用 2. 不动态调整蒸馏损失 2.1 训练定量化结果 2.1 训练结果可视化结果 3. 动态调整蒸馏损失权重及实验分析 3.1 余弦衰减和指数衰减 3.2 CWD蒸馏损失 3.3 MGD蒸馏损失 3.4 AT蒸馏损失 3.5 SKD和PKD蒸馏损失 4. 调权重心得总结 5…

历年华东师范大学保研上机真题

2025华东师范大学保研上机真题 2024华东师范大学保研上机真题 2023华东师范大学保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school?classification1 简单一位数代数式计算 题目描述 给一个小学生都会算的1位数与1位数运算的代数式&#xff0c;请你求出这个表…

Oracle 中 SHRINK 与 MOVE 操作的比较

Oracle 中 SHRINK 与 MOVE 操作的比较 在 Oracle 数据库中&#xff0c;SHRINK 和 MOVE 都是用于重组表和索引以减少空间碎片的重要操作&#xff0c;但它们在实现方式和适用场景上有显著区别。 SHRINK 操作 基本语法 ALTER TABLE table_name SHRINK SPACE [COMPACT] [CASCAD…

展锐 Android 15 锁定某个App版本的实现

Android 15 系统锁定Antutu版本的实现方法 在Android系统开发中,有时需要锁定特定应用的版本以确保系统稳定性或测试一致性。本文将介绍如何通过修改Android源码来锁定Antutu跑分软件的版本。 修改概述 这次修改主要涉及以下几个方面: 禁用产品复制文件的检查添加指定版本…

视频剪辑SDK定制开发技术方案与报价书优雅草卓伊凡

视频剪辑SDK定制开发技术方案与报价书-优雅草卓伊凡 一、项目概述 客户需求&#xff1a;开发一套跨平台&#xff08;Android/iOS/Uni-App&#xff09;视频剪辑SDK&#xff0c;包含AI字幕提取、转场特效、文字叠加、背景音乐、滤镜、背景替换、动态贴纸等功能。 报价范围&#…

BGP为什么要配置对等IP?

本文由deepseek生成&#xff0c;特此声明 一、为什么要配置对等体IP&#xff1f; 1. 明确标识邻居身份 路由协议需求&#xff1a;动态路由协议&#xff08;如BGP、OSPF、RIP&#xff09;需要路由器之间建立邻居关系以交换路由信息。配置对等体IP是为了唯一标识邻居路由器&…

Qt中配置文件读写

1. 保存分组数据到配置文件 #include <QSettings>void saveNetworkConfig() {QSettings settings("network.ini", QSettings::IniFormat);// 网络配置分组settings.beginGroup("Network");// 源地址配置settings.beginGroup("Source");se…

Linux 的编辑器--vim

1.Linux编辑器-vim使⽤ vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;⽽且还有⼀些新的特性在⾥⾯。例如语法加亮&#xff0c;可视化操作不仅可以在终端运⾏&#xff0c;也可以…

SAP Commerce(Hybris)开发实战(二):登陆生成token问题

问题简述 最近处理Hybris框架标准的登陆功能&#xff0c;遇到一个问题&#xff1a;用两个不同的浏览器&#xff0c;同时登陆一个账号&#xff0c;会同时生成两个不同的token和refreshToken。 问题原因 解决了其实非常简单&#xff0c;就是Hybris的Employee表中&#xff0c;有一…

c/c++的opencv椒盐噪声

在 C/C 中实现椒盐噪声 椒盐噪声&#xff08;Salt-and-Pepper Noise&#xff09;&#xff0c;也称为脉冲噪声&#xff08;Impulse Noise&#xff09;&#xff0c;是数字图像中常见的一种噪声类型。它的特点是在图像中随机出现纯白色&#xff08;盐&#xff09;或纯黑色&#x…

LIEDNet: A Lightweight Network for Low-light Enhancement and Deblurring论文阅读

摘要 夜间拍摄的图像常常面临诸如低光和模糊等挑战&#xff0c;这些问题主要是由于昏暗环境和长时间曝光的频繁使用所导致。现有方法要么独立处理这两种退化问题&#xff0c;要么依赖于通过复杂机制生成的精心设计的先验知识&#xff0c;这导致了较差的泛化能力和较高的模型复…

谈谈worldquant中设置的几个意思

Decay 是一个设置&#xff0c;用于确定要反映多少过去的位置。正如我们之前详细介绍的那样&#xff0c;Decay 值越高&#xff0c;Alpha 周转率越低。但是&#xff0c;请注意&#xff0c;Alpha 的夏普比率可能会随着信息延迟而降低。 创建 Alpha 时&#xff0c;头寸可能会集中在…

大模型和AI工具汇总(一)

一、国内可免费使用的大模型&#xff08;持续更新&#xff09; DeepSeek 模型介绍&#xff1a;DeepSeek 系列包括 DeepSeek V3&#xff08;通用场景&#xff09;、DeepSeek R1&#xff08;推理模型&#xff09;&#xff0c;支持高达 64K 上下文长度&#xff0c;中文场景表现优…

HarmonyOS NEXT 技术特性:分布式软总线技术架构

HarmonyOS NEXT 技术特性&#xff1a;分布式软总线技术架构 随着物联网发展&#xff0c;2030 预计全球联网设备达 2000 亿&#xff0c;异构设备互联难题凸显&#xff0c;分布式软总线作为 HarmonyOS 生态核心&#xff0c;以软件虚拟总线打破物理局限&#xff0c;让跨品牌设备即…

什么是VR展馆?VR展馆的实用价值有哪些?

VR展馆&#xff0c;重塑展览体验。在数字化时代浪潮的推动下&#xff0c;传统的实体展馆经历前所未有的变革。作为变革的先锋&#xff0c;VR展馆以无限的潜力&#xff0c;成为展览行业的新宠。 VR展馆&#xff0c;即虚拟现实展馆&#xff0c;是基于VR&#xff08;Virtual Real…

VLA模型:自动驾驶与机器人行业的革命性跃迁,端到端智能如何重塑未来?

当AI开始操控方向盘和机械臂&#xff0c;人类正在见证一场静默的产业革命。 2023年7月&#xff0c;谷歌DeepMind抛出一枚技术核弹——全球首个视觉语言动作模型&#xff08;VLA&#xff09;RT-2横空出世。这个能将“把咖啡递给穿红衣服的阿姨”这类自然语言指令直接转化为机器人…

华为OD机试真题——出租车计费/靠谱的车 (2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

40 岁 Windows 开启 AI 转型:从系统到生态的智能重构

在科技快速发展的当下&#xff0c;人工智能成为驱动各领域变革的核心力量&#xff0c;拥有 40 年历史的 Windows 也开启了向 AI 的全面转型。2025 年 5 月 19-22 日西雅图 Build 2025 开发者大会上&#xff0c;微软展示了 Windows 11 向 AI 智能体核心平台转型的战略&#xff0…

Python实例题:Python3实现可控制肉鸡的反向Shell

目录 Python实例题 题目 代码实现 reverse_shell_client.py reverse_shell_server.py 实现原理 反向连接机制&#xff1a; 命令执行与传输&#xff1a; 功能特点&#xff1a; 关键代码解析 服务端命令处理 客户端命令执行 客户端持久化连接 使用说明 启动服务端…

AWS EC2 使用Splunk DB connect 连接 RDS mysql

1: 先创建 RDS mysql: 我们选择free: 选择free 过后,自动生成single instance, 没有垮AZ 的db 设置。 选择密码登入: 注意:上面设置密码的时候,特别提示:不能有特殊字符,我就设置了: mypassword 下面可以选择通过EC2 连接,当然也可以不选: