插件窝 干货文章 linux服务器开发—手写内存泄漏检测组件

linux服务器开发—手写内存泄漏检测组件

mem size info mutex 971    来源:    2025-05-14

Linux服务器开发 - 手写内存泄漏检测组件

内存泄漏是C/C++服务器开发中常见的问题之一,特别是在长时间运行的服务器程序中。下面我将介绍如何手写一个内存泄漏检测组件。

基本原理

内存泄漏检测的基本原理是: 1. 重载malloc/free等内存分配函数 2. 记录每次内存分配和释放的信息 3. 在程序结束时检查未释放的内存块

实现方案

1. 基本数据结构

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

typedef struct _mem_info {
    void* ptr;              // 分配的内存指针
    size_t size;            // 分配的大小
    const char* file;       // 分配所在的文件
    int line;               // 分配所在的行号
    struct _mem_info* next; // 下一个节点
} mem_info;

static mem_info* mem_list = NULL;
static pthread_mutex_t mem_mutex = PTHREAD_MUTEX_INITIALIZER;

2. 重载内存分配函数

void* my_malloc(size_t size, const char* file, int line) {
    void* ptr = malloc(size);
    if (ptr != NULL) {
        mem_info* info = (mem_info*)malloc(sizeof(mem_info));
        info->ptr = ptr;
        info->size = size;
        info->file = file;
        info->line = line;

        pthread_mutex_lock(&mem_mutex);
        info->next = mem_list;
        mem_list = info;
        pthread_mutex_unlock(&mem_mutex);
    }
    return ptr;
}

void my_free(void* ptr) {
    if (ptr == NULL) return;

    pthread_mutex_lock(&mem_mutex);
    mem_info** p = &mem_list;
    while (*p != NULL) {
        if ((*p)->ptr == ptr) {
            mem_info* tmp = *p;
            *p = (*p)->next;
            free(tmp);
            free(ptr);
            pthread_mutex_unlock(&mem_mutex);
            return;
        }
        p = &((*p)->next);
    }
    pthread_mutex_unlock(&mem_mutex);

    // 未找到对应的分配记录
    fprintf(stderr, "Invalid free: %p\n", ptr);
    free(ptr); // 仍然释放,避免崩溃
}

#define malloc(size) my_malloc(size, __FILE__, __LINE__)
#define free(ptr) my_free(ptr)

3. 检测并报告内存泄漏

void check_leaks() {
    pthread_mutex_lock(&mem_mutex);
    mem_info* p = mem_list;
    if (p == NULL) {
        printf("No memory leaks detected.\n");
    } else {
        printf("Memory leaks detected:\n");
        size_t total_leak = 0;
        int leak_count = 0;

        while (p != NULL) {
            printf("Leak %d: %zu bytes at %p, allocated at %s:%d\n", 
                   ++leak_count, p->size, p->ptr, p->file, p->line);
            total_leak += p->size;
            mem_info* tmp = p;
            p = p->next;
            free(tmp->ptr); // 释放泄漏的内存
            free(tmp);      // 释放记录节点
        }

        printf("Total %d leaks, %zu bytes\n", leak_count, total_leak);
        mem_list = NULL;
    }
    pthread_mutex_unlock(&mem_mutex);
}

4. 使用示例

#include "leak_detector.h"

int main() {
    // 在程序退出前注册泄漏检测
    atexit(check_leaks);

    // 测试代码
    int* p1 = (int*)malloc(sizeof(int)*10);
    char* p2 = (char*)malloc(100);

    free(p1); // 故意不释放p2

    return 0;
}

高级功能扩展

1. 支持calloc/realloc

void* my_calloc(size_t nmemb, size_t size, const char* file, int line) {
    void* ptr = calloc(nmemb, size);
    if (ptr != NULL) {
        mem_info* info = (mem_info*)malloc(sizeof(mem_info));
        info->ptr = ptr;
        info->size = nmemb * size;
        info->file = file;
        info->line = line;

        pthread_mutex_lock(&mem_mutex);
        info->next = mem_list;
        mem_list = info;
        pthread_mutex_unlock(&mem_mutex);
    }
    return ptr;
}

void* my_realloc(void* ptr, size_t size, const char* file, int line) {
    if (ptr == NULL) {
        return my_malloc(size, file, line);
    }

    pthread_mutex_lock(&mem_mutex);
    mem_info** p = &mem_list;
    while (*p != NULL) {
        if ((*p)->ptr == ptr) {
            void* new_ptr = realloc(ptr, size);
            if (new_ptr != NULL) {
                (*p)->ptr = new_ptr;
                (*p)->size = size;
                (*p)->file = file;
                (*p)->line = line;
            }
            pthread_mutex_unlock(&mem_mutex);
            return new_ptr;
        }
        p = &((*p)->next);
    }
    pthread_mutex_unlock(&mem_mutex);

    // 未找到原始分配记录
    fprintf(stderr, "Invalid realloc: %p\n", ptr);
    return realloc(ptr, size);
}

#define calloc(nmemb, size) my_calloc(nmemb, size, __FILE__, __LINE__)
#define realloc(ptr, size) my_realloc(ptr, size, __FILE__, __LINE__)

2. 线程安全改进

使用线程局部存储来支持多线程环境:

static __thread mem_info* thread_mem_list = NULL;

// 修改my_malloc/my_free等函数使用thread_mem_list代替mem_list
// 在check_leaks中需要遍历所有线程的内存列表

3. 性能优化

使用哈希表代替链表可以提高查找效率:

#define HASH_SIZE 1024

typedef struct _mem_info {
    void* ptr;
    size_t size;
    const char* file;
    int line;
    struct _mem_info* next;
} mem_info;

static mem_info* mem_hash[HASH_SIZE];
static pthread_mutex_t mem_mutex[HASH_SIZE];

static inline unsigned int hash_ptr(void* ptr) {
    return ((unsigned long)ptr >> 4) % HASH_SIZE;
}

// 修改my_malloc/my_free使用哈希表存储

使用注意事项

  1. 需要在所有源文件中包含此头文件,确保所有内存分配都被跟踪
  2. 对于第三方库的内存分配,可能需要特殊处理
  3. 在生产环境中可能会影响性能,建议仅在调试时使用
  4. 对于多线程程序,确保线程安全

替代方案

如果不想自己实现,也可以考虑以下成熟工具: - Valgrind - AddressSanitizer (ASan) - mtrace (glibc自带工具)

这个手写的内存泄漏检测组件虽然简单,但包含了基本原理,可以根据项目需求进行扩展和优化。