作为C/C++程序员,谁还不写Bug,Bug中最常见要数内存泄漏、内存崩溃、死锁等问题,本文主要讲解内存泄漏问题,其具有其独有的属性,比如说:隐蔽性强、难以排查、占用资源不断累积等特点,当到达一定程度会出现崩溃、被系统kill掉等……
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
场景1:new/malloc与delete/malloc没有成对出现
void callfun(char *p)
{
std::cout<< p<<std::endl;
}
void fun()
{
char *p = new char[128];
return callfun(p);
}
场景2:new/malloc 提前返回
void fun(std::string request)
{
char *p = new char[128];
if(request.length()== 0){
return;
}
delete []p;
return;
}
场景3:基类被继承时,虚构函数没有被定义为virtual
class A
{
public:
A(){}
~A(){} //未定义virutal
};
class B:public A
{
public:
B(){
p = new char[128];
}
~B(){
if(p) delete [] p;
p = nullptr;
}
private:
char *p = nullptr;
};
int main()
{
A* inst = new B;
...
delete inst;
return 0;
}
场景4:类对象通过shared_ptr<>相互引用
class A
{
public:
A() {}
~A() {}
publpic:
int _data = 0;
std::shared_ptr<A> _next;
}
int main()
{
shared_ptr<A> n1(new A);
shared_ptr<A> n2(new A);
n1.get()->_next = n2; // 代表深蓝色的线所做的事(n1结点的下一个指向n2)
n2.get()->_next = n1; // 代表浅蓝色的线所做的事(n2结点的下一个指向n1)
return 0;
}
场景5:引用第三方接口分配资源,没有调用释放资源接口
char *mallocresource(int len);
void demallocresource(char *p)
int fun()
{
char* p = mallocresource(100);
//do something
return 0;
}
场景6:未关闭文件句柄
int readFile()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL){
perror("fopen");//输出错误原因,双引号里为要检查的函数
return -1;
}
fseek(pf, 9, SEEK_SET);//文件指针偏移 SEEK_SET初始位置开始偏移
int ch=fgetc(pf); //SEEK_CUR 当前位置开始偏移
printf("%c\n", ch); //SEEK_END 末尾位置开始偏移
return 0;
}
场景7:释放对象数组时在delete中没有使用方括号
class A
{
public:
A(){
p = new char[128];
}
~A(){
if(p) delete p;
p = nullptr;
}
public:
char *p;
};
int func()
{
A* P = new A[100];
delete p;
return 0;
}
场景8:stl中value指针没有被delete释放
class A
{
public:
A(){
}
~A(){
}
};
std::map<std::string,A*> Avec;
void fun(){
Avec.emplace("1",new A());
***
Avec.clear();
}
如何规避内存泄漏问题
1、通过智能指针来规避大部分内存泄漏问题
如何定位内存泄漏问题
1、通过代码打印,打桩封装malloc与free等接口,发现哪个模块没有内存分配没有成对(麻烦不推荐)
2、仔细评审两个版本间的代码差异,看看哪个地方疏忽等原因引入了内存泄漏,工作量打不推荐。
2、valgrind工具来定位、或asan工具等,细节略。
通过上面的介绍,应该对常见的内存泄漏问题有了一点的了解,希望对你以后开发的过程中有所帮助。