C++代码内存泄漏检测的思路

C++代码内存泄漏检测的思路

C++当中,如果不使用智能的指针的情况下,内存管理将是个大问题。C++语言层面并没有垃圾回收机制(GC),如何在项目中检测内存泄漏问题呢?

我们先来看看

1.内存泄漏的产生是new操作后,没有执行delete。然而new和delete都是操作符,我们可以使用操作符重载。在new时记录相关信息,在delete时删除该信息。

2.需要记录和删除信息,那么我们可以创建一个对象保存new时的必要信息,并将其作为value, 申请空间的地址作为key保存在K-V容器(unordered_map)中。

3.此时我们需要一个类去管理所有的动态内存分配的数据信息,并且这个类最好设计为单例模式。

memcheck.h

#ifndef _MEMCHEKC_H_
#define _MEMCHEKC_H_
#include<iostream>
#include<unordered_map>
 
struct MemInfo//存的是数据!new时的数据!
{
    MemInfo() = default;
    MemInfo(const char* mFile, unsigned long mLine, unsigned long mSize)
    :file(mFile), line(mLine), size(mSize)
    {
    }
 
    std::string file;//那个文件的程序创建的
    unsigned long line;//那一行创建的
    unsigned long size;//创建了多大
};
 
//单例模式
class MemCheck{
private:
    static bool notfreememCheck;//判断memcheck是否被释放,如果被释放了,是不允许调用的
    static std::unordered_map<void*, MemInfo> _memPool;//存储每一个new对象的类型,和他所对应的MemInfo数据
    unsigned long _totalMemSize;//计算申请了的堆大小
    static MemCheck memcheck;//定义一个类对象自己!
    //这里只是申明,类属性需要定义。注意定义顺序非常重要
 
public:
    MemCheck();
    ~MemCheck();
    
    //每次new了的数据都要添加到_memPool中记录!
    static void setMemInfo(void* p, const char* file, unsigned long line, unsigned long size);
    //从记录中删除!
    static void deleteMemInfo(void* p);
};
 
//对new和new[]进行重载!
void* operator new(size_t size, const char* file, unsigned long line);
void* operator new[](size_t size, const char* file, unsigned long line);
 
//对delete和delete[]进行重载!
void operator delete(void* ptr);
void operator delete[](void* ptr);
 
//对new进行宏定义,每次调用时,自动将参数file和line加入!
//这个宏定义会导致其他的文件new产生问题!
#ifndef _NEW_OVERLOAD_IMPLEMENTATION_
#define new new(__FILE__, __LINE__)
#endif
 
#endif

memcheck.cpp

//定义这个宏,一定要加到抬头,防止对memcheck.h和memcheck.cpp进行new展开替换
#define _NEW_OVERLOAD_IMPLEMENTATION_
 
//引入头文件
#include"memcheck.h"
 
//这些类属性,都是放在静态全局区的,所以在释放的时候一定要注意入栈出栈的顺序
//memcheck要最后定义,才会最先释放,然后去调用析构
bool MemCheck::notfreememCheck = true;
std::unordered_map<void*, MemInfo> MemCheck::_memPool;
MemCheck MemCheck::memcheck;
 
MemCheck::MemCheck(){
 
}
 
//因为没有new都是MemInfo对象,所以不需要释放空间
MemCheck::~MemCheck(){
    memcheck.notfreememCheck = false;
 
    std::cout << "------MEMCHECK DESTRUCTOR------\n";
    if(memcheck._memPool.size() > 0){//说明unordered_map内数据,有内存泄漏!
        std::cout << "------leak memory info------\n";
        for (auto it : _memPool){//这里相当于取map对象,所以用 . 访问成员函数
            std::cout << "Leak Memory Address [ " << it.first << "] ";//first是map的第一个参数或者说叫键!void*
            std::cout << "File [ " << it.second.file << "] ";//second是map的第二个参数或者说叫值!MemInfo
            std::cout << "Line [ " << it.second.line << "] ";
            std::cout << "size [ " << it.second.size << "]\n";
        }
        std::cout << "------leak memory info------\n";
    }
}
 
void MemCheck::setMemInfo(void* p, const char* file, unsigned long line, unsigned long size){//添加记录!
    if(!notfreememCheck) {return;}
    MemInfo pInfo(file, line, size);
    memcheck._totalMemSize += size;
    memcheck._memPool[p] = pInfo;
}

void MemCheck::deleteMemInfo(void* p){//删除记录
    if(!notfreememCheck) {return;}
 
    auto pInfo = memcheck._memPool.find(p);
    if(pInfo != memcheck._memPool.end()){
        memcheck._totalMemSize -= pInfo->second.size;
        memcheck._memPool.erase(pInfo);
    }
}

void* operator new(size_t size, const char* file, unsigned long line){
    if(size == 0) size = 1;
    void* ptr = malloc(size);
    if(ptr == nullptr){
        std::cout << "ERR NEWE[]!" << std::endl;
    }else{
        //记录
        MemCheck::setMemInfo(ptr, file, line, size);
    }

    return ptr;
}

void* operator new[](size_t size, const char* file, unsigned long line){
    if(size == 0) size = 1;
    void* ptr = malloc(size);
    if(ptr == nullptr){
        std::cout << "ERR NEWE[]!" << std::endl;
    }else{
        //记录
        MemCheck::setMemInfo(ptr, file, line, size);
    }

    return ptr;
}

void operator delete(void* ptr){
    MemCheck::deleteMemInfo(ptr);

    if(ptr) free(ptr);
}
 
void operator delete[](void* ptr){
    MemCheck::deleteMemInfo(ptr);

    if(ptr) free(ptr);
}

main.cpp

#include<iostream>
#include<map>
#include"memcheck.h"
 
using namespace std;

class TEST{};
 
int main(){
    TEST* p = new TEST;
    
    return 0;
}

注意:

1.单例模式中的类属性static std::unordered_map<void*, MemInfo> _memPool;和static MemCheck memcheck;在定义时一定要注意其顺序!其保存在静态全局区,释放时以栈的顺序出栈释放,所以必须要将memcheck定义在最后,这样在释放时就优先释放,调用其析构,保证程序的正确执行。如果map被先释放了,再调用析构,那么程序就完全错误了。

2.在memcheck.h中!宏定义了new,所以在main函数中,引入其头文件一定要放在最后,避免对其他文件造成影响。

该文章会更新,欢迎大家批评指正。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值