C 通过宏定义重定义malloc - free,预防内存泄露

简介: C 通过宏定义重定义malloc - free,预防内存泄露

代码地址

GitHub - CHENLitterWhite/CPPWheel: CPP自封装的库

/*
*    作者: 干饭小白
*    时间: 2023-09-25 16:00:00:00
*    
*    说明:
*      只能检测 malloc 和 free,无法检测 new delete
*/
#pragma once
// #define CHECKMEM
#define OPEN_TOTAL_INFO    0x001
#define OPEN_SINGLE_INFO   0x010
#define OPEN_THREAD_INFO   0x100
#include <map>
#include <string>
#include <cstddef>
#include <utility>
#ifdef __cplusplus
extern "C"
{
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <semaphore.h>
};
#endif
struct _gc_info
{
  long _size;
  std::string _content;
  _gc_info()
  {
  }
  _gc_info(long size, std::string con)
  {
    _size = size;
    _content = con;
  }
};
static char Logpath[256] = {0};
static char DirPath[256] = {0};
static std::map<std::string ,_gc_info> total;
volatile static long totalMalloc = 0;
volatile static long totalFree = 0;
volatile static long totalMallocSize = 0;
volatile static long totalFreeSize = 0;
static uint16_t Mode = false;
static sem_t sem_mutex;
extern void get_time(char* str)
{
  time_t now;
  struct tm *local_time;
  now = time(NULL);
  local_time = localtime(&now);
  sprintf(str, "%d-%02d-%02d_%02d:%02d:%02d", 
            local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,
            local_time->tm_hour, local_time->tm_min, local_time->tm_sec);
  return;
}
extern void init_gc(const char* dirPath, uint16_t mode = OPEN_TOTAL_INFO)
{
    sem_init(&sem_mutex, 0, 1);
    struct stat st;
    if (stat(dirPath, &st) == 0 && S_ISDIR(st.st_mode))
    {
        // printf("文件夹已存在\n");
    }
    else 
    {
        if(mkdir(dirPath, 0777) != 0)
        {
            return;
        } 
    }
    Mode = mode;
    strncpy(DirPath, dirPath, 256);
    char time[256] = {0};
    get_time(time);
    sprintf(Logpath, "%s/%s_total.log", dirPath, time);
    unlink(Logpath);
    char buff[128] = {0};
    sprintf(buff, "%s/thread.log", DirPath);  
    unlink(buff);
    return;
}
extern void total_gc()
{
    sem_destroy (&sem_mutex);
    if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
    {
        FILE *fp = fopen(Logpath, "a+w");
        fprintf(fp, "\n=========================================\n");
        fprintf(fp, "申请次数:%ld\n", totalMalloc);
        fprintf(fp, "释放次数:%ld\n", totalFree);
        fprintf(fp, "申请总大小:%ld\n", totalMallocSize);
        fprintf(fp, "释放总大小:%ld\n", totalFreeSize);
        fprintf(fp, "未释放总大小:%ld\n", totalMallocSize - totalFreeSize);
        fprintf(fp, "问题统计:%ld\n", total.size());
        fprintf(fp, "=========================================\n");
        fflush(fp);
        for(auto it = total.begin(); it != total.end(); ++it)
        {
            fprintf(fp, "%s", it->second._content.c_str());
            fflush(fp);
        }
        fclose(fp);
    }
    total.clear();
}
extern void* malloc_gc(size_t size, const char* file, const char* func, int line)
{
    sem_wait(&sem_mutex);
    FILE *test = NULL;
    if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
    {   
        char buff[128] = {0};
        sprintf(buff, "%s/thread.log", DirPath);
        test = fopen(buff, "a+w");
        fprintf(test, "ERROR 进入malloc_gc--->%ld\n", pthread_self());
        fflush(test);  
    }
    void* p =malloc(size);
    char time[256] = {0};
    get_time(time);
    FILE *fp = NULL;
    if((Mode & OPEN_SINGLE_INFO) == OPEN_SINGLE_INFO)
    {
        char buff[128] = {0};
        sprintf(buff, "%s/%p.log", DirPath,p);
        FILE *fp = fopen(buff, "w");
        fprintf(fp, "%s:ERROR [+%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self() , p, size);
        fflush(fp);
        fclose(fp);
    }
    if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
    {
        totalMallocSize+= size;
        fp = fopen(Logpath, "a+w");
        fprintf(fp, "%s:[+%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, size);
        fflush(fp);
        fclose(fp);
        char content[256] = {0};
        sprintf(content, "%s:ERROR [+!%s:%s:%d][PID:%ld] 内存泄漏 --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, size);
        char addr[128] = {0};
        sprintf(addr, "%p", p);
        _gc_info info(size, content);
        total.insert(std::make_pair(addr, info));
        totalMalloc++;
    }
    if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
    { 
        fprintf(test, "INFO  离开malloc_gc--->%ld\n", pthread_self());
        fflush(test);  
        fclose(test);
    }
    sem_post(&sem_mutex);
    return p;
}
extern void free_gc(void* p, const char* file, const char* func, int line)
{
    sem_wait(&sem_mutex);  
    FILE *test = NULL;
    if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
    { 
        char buff[128] = {0};
        sprintf(buff, "%s/thread.log", DirPath);
        test = fopen(buff, "a+w");
        fprintf(test, "ERROR 进入free_gc--->%ld\n", pthread_self());
        fflush(test);  
    }
    char time[256] = {0};
    get_time(time);
    if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
    {
        totalFree++;
        char addr[128] = {0};
        sprintf(addr, "%p", p);
        if(total.find(addr) == total.end())
        {
            char content[256] = {0};
            sprintf(content, "%s:ERROR [-!%s:%s:%d][PID:%ld] 二次释放 --> ADDR:%p\n", time, file, func, line, pthread_self(), p);
            char addr[128] = {0};
            sprintf(addr, "%p", p);
            _gc_info info(0, content);
            sem_post(&sem_mutex);
            total.insert(std::make_pair(addr, info));
        }
        else 
        {
            totalFreeSize += total[addr]._size;
            FILE* fp = fopen(Logpath, "a+w");
            fprintf(fp, "%s:[-%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, total[addr]._size);
            fflush(fp);
            fclose(fp);
            total.erase(addr);
            free(p);
        }
    }
    char buff[128] = {0};
    sprintf(buff, "%s/%p.log", DirPath,p);
    if((Mode & OPEN_SINGLE_INFO) == OPEN_SINGLE_INFO && unlink(buff) < 0)
    {
        FILE *fp = fopen(buff, "w");
        fprintf(fp, "%s:ERROR [-!%s:%s:%d][PID:%ld] 二次释放 --> ADDR:%p\n", time, file, func, line, pthread_self(), p);
        fflush(fp);
        fclose(fp);
    }
    if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
    {
        fprintf(test, "INFO  离开free_gc--->%ld\n", pthread_self());
        fflush(test);  
        fclose(test);
    }
    sem_post(&sem_mutex);
    return;
}
#ifdef CHECKMEM
#define malloc(size) malloc_gc(size, __FILE__, __FUNCTION__, __LINE__)
#define free(p) free_gc(p, __FILE__, __FUNCTION__, __LINE__)
#endif

相关说明

1.不支持new 和 delete。尝试过,发现效果不好,new本身内部会malloc和free,想要支持多线程,容易加锁两次。delete不方便宏替换。

2.支持多线程,不支持多进程,如果想要支持多进程,相关的控制部分定义到共享内存中

3.定义CHECKMEM,开启内存检查

使用案例

#include "mem.h"
int main()
{
    init_gc("./mem", OPEN_TOTAL_INFO | OPEN_SINGLE_INFO | OPEN_THREAD_INFO);
    void* p1 = malloc(1);
    void* p2 = malloc(1);
    void* p3 = malloc(1);
    free(p1);
    free(p2);
    free(p2);
    total_gc();
    return 0;
}

部分效果图

多线程下定位具体线程 ==> OPEN_THREAD_INFO

统计信息 ==> OPEN_TOTAL_INFO

单文件快速定位 ==> OPEN_SINGLE_INFO

相关文章
|
5月前
|
Java 数据库连接
Java中的内存泄漏排查与预防方法
Java中的内存泄漏排查与预防方法
|
20天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
40 6
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
211 1
|
2月前
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
77 0
|
3月前
|
Arthas 监控 Java
监控线程池的内存使用情况以预防内存泄漏
监控线程池的内存使用情况以预防内存泄漏
|
4月前
|
程序员 C++
malloc与free的内存管理奥秘:技术分享
【8月更文挑战第22天】在软件开发过程中,内存管理是一个至关重要的环节。特别是在使用C或C++这类语言时,程序员需要手动管理内存的分配与释放。malloc和free函数是这一过程中的核心工具。本文将深入探讨malloc如何分配内存,以及free如何知道释放多少内存,帮助你在工作学习中更好地掌握这一技术干货。
101 4
|
4月前
|
Linux 测试技术 C++
内存管理优化:内存泄漏检测与预防。
内存管理优化:内存泄漏检测与预防。
63 2
|
4月前
|
存储 算法 安全
实现一个malloc内存分配器
需要注意的是,这个例子是一个非常简单的实现,它只是为了演示原理,对于一个强壮的、用于生产环境的内存分配器来说还远远不够。一个成熟的分配器将考虑到多线程的同步、更复杂的内存分配算法、碎片整理策略、错误处理,以及安全性问题,比如防止缓冲区溢出。在实际应用程序中,你应该使用标准库提供的或操作系统提供的内存分配器,除非你确实需要并且能够处理自己实现内存分配器所带来的复杂性。
44 3
|
5月前
|
开发者 Java
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
JVM内存问题之top命令的物理内存信息中,'used'和'free','avail Mem'分别表示什么
|
5月前
|
存储 编译器 程序员
【C/C++】动态内存管理(C:malloc,realloc,calloc,free)
探索C++与C语言的动态内存管理:从malloc到new/delete,了解内存分布及栈、堆的区别。文章涵盖malloc、realloc、calloc与free在C中的使用,强调内存泄漏的风险。C++引入new和delete,支持对象构造与析构,还包括operator new和placement-new。深入分析内存管理机制,揭示C与C++在内存处理上的异同。别忘了,正确释放内存至关重要!