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++服务器