You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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运行测试程序,精准检测泄漏:
    valgrind --leak-check=full ./your_test_program
    
    若输出“All heap blocks were freed -- no leaks are possible”,说明内存已正确释放,只是系统未立刻回收。
  • 也可以调用malloc_stats()或mallinfo()(glibc专属)打印内存分配器统计,查看已释放但未归还系统的内存量。

4. 额外优化建议

  • 大规模队列操作可实现内存池,预先分配一批节点,避免频繁malloc/free,提升性能同时减少内存碎片。
  • 测试时先缩小元素数量(比如1000个),用valgrind快速验证内存逻辑,再放大到2亿规模。

内容的提问来源于stack exchange,提问作者lzg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:59:51