C语言简单队列实现中出现诡异内存泄漏问题排查
链表队列内存未释放问题排查
问题背景
我给C库实现了一个带enqueue()和dequeue()的链表队列,功能测试全部正常无报错,但入队2亿个元素再全部出队后,内存似乎未被释放,不过队列状态更新正常。
入队/出队函数
usf_queue *usf_enqueue(usf_queue *queue, usf_data d) { //Enqueue to FIFO queue if (queue == NULL) { //Generate new queue queue = (usf_queue *) malloc(sizeof(usf_queue)); if (queue == NULL) return NULL; //Memory allocation failure //Empty at first queue -> first = queue -> last = NULL; } usf_queuenode *append = (usf_queuenode *) malloc(sizeof(usf_queuenode)); if (append == NULL) return NULL; //Memory allocation failure append -> data = d; append -> next = NULL; //Last in line if (queue -> last == NULL) //Was empty queue -> first = queue -> last = append; else { //Normal adding queue -> last -> next = append; queue -> last = append; } return queue; } usf_data usf_dequeue(usf_queue *queue) { //Dequeue from FIFO queue usf_data d; usf_queuenode *old; if (queue -> first == NULL || queue == NULL) return (usf_data) { .p = NULL }; //No queue, or queue is empty old = queue -> first; d = old -> data; //Get data if ((queue -> first = old -> next) == NULL) queue -> last = NULL; free(old); //Destroy dequeued node return d; }
队列结构体定义
typedef union usf_data { void *p; uint64_t u; int64_t i; } usf_data; typedef struct usf_queuenode { usf_data data; struct usf_queuenode *next; } usf_queuenode; typedef struct usf_queue { usf_queuenode *first; usf_queuenode *last; } usf_queue;
测试代码
#include "usflib2.h" #include <stdio.h> int main() { usf_queue *q = NULL; q = usf_enqueue(q, (usf_data) {.u = -1 }); for (int i = 1; i < 200000000; i++) usf_enqueue(q, (usf_data) {.u = i}); printf("Done.\n"); for (int i = 0; i < 200000000; i++) { usf_dequeue(q); } free(q); printf("Finished\n"); for(;;); return 0; }
问题排查与解决
1. 先修复空指针访问隐患
usf_dequeue()的判断顺序错误:先访问queue->first再检查queue == NULL,如果传入NULL会直接触发未定义行为,调整判断顺序:
if (queue == NULL || queue -> first == NULL) return (usf_data) { .p = NULL };
2. 内存未释放是“视觉错觉”
你看到的内存未释放,本质是操作系统和C标准库的内存管理机制导致:
- 调用
free()后,内存不会立刻归还给操作系统,而是被内存分配器(比如glibc的ptmalloc)保留在进程内存池中,用于后续分配请求——频繁向系统申请/释放内存开销极大,分配器会优先复用已释放的内存。 - 2亿次
malloc/free的规模下,分配器会倾向于保留这批内存块,避免重复系统调用。
3. 验证内存是否真泄漏
用专业工具确认:
- 用
valgrind运行测试程序,精准检测泄漏:
若输出“All heap blocks were freed -- no leaks are possible”,说明内存已正确释放,只是系统未立刻回收。valgrind --leak-check=full ./your_test_program - 也可以调用
malloc_stats()或mallinfo()(glibc专属)打印内存分配器统计,查看已释放但未归还系统的内存量。
4. 额外优化建议
- 大规模队列操作可实现内存池,预先分配一批节点,避免频繁
malloc/free,提升性能同时减少内存碎片。 - 测试时先缩小元素数量(比如1000个),用
valgrind快速验证内存逻辑,再放大到2亿规模。
内容的提问来源于stack exchange,提问作者lzg
相关产品推荐
相关产品推荐

