1、为什么会内存泄漏
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
在C语言中,从变量存在的时间生命周期角度上,把变量分为静态存储变量和动态存储变量两类。静态存储变量是指在程序运行期间分配了固定存储空间的变量而动态存储变量是指在程序运行期间根据实际需要进行动态地分配存储空间的变量。在内存中供用户使用的内存空间分为三部分:
-
程序存储区
-
静态存储区
-
动态存储区
程序中所用的数据分别存放在静态存储区和动态存储区中。静态存储区数据在程序的开始就分配好内存区,在整个程序执行过程中它们所占的存储单元是固定的,在程序结束时就释放,因此静态存储区数据一般为全局变量。动态存储区数据则是在程序执行过程中根据需要动态分配和动态释放的存储单元,动态存储区数据有三类函数形参变量、局部变量和函数调用时的现场保护与返回地址。由于动态存储变量可以根据函数调用的需要,动态地分配和释放存储空间,大大提高了内存的使用效率,使得动态存储变量在程序中被广泛使用。
2、内存泄漏后果
内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。内存泄漏可能不严重,甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。
在以下情況,内存泄漏导致较严重的后果:
- 程序运行后置之不理,并且随着时间的流失消耗越来越多的内存(比如服务器上的后台任务,尤其是嵌入式系统中的后台任务,这些任务可能被运行后很多年内都置之不理);
- 新的内存被频繁地分配,比如当显示电脑游戏或动画视频画面时;
- 程序能够请求未被释放的内存(比如共享内存),甚至是在程序终止的时候;
- 泄漏在操作系统内部发生;
- 泄漏在系统关键驱动中发生;
- 内存非常有限,比如在嵌入式系统或便携设备中;
- 当运行于一个终止时内存并不自动释放的操作系统(比如AmigaOS)之上,而且一旦丢失只能通过重启来恢复。
Windows平台下的内存泄漏检测
Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using namespace std;
void GetMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char) * num);
}
int main(int argc,char** argv)
{
char *str = NULL;
GetMemory(str, 100);
cout<<"Memory leak test!"<<endl;
_CrtDumpMemoryLeaks();
return 0;
}
这种方法只是可以定位到行定位不到具体函数位置
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
using namespace std;
_CrtMemState s1, s2, s3;
void GetMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char) * num);
}
int main(int argc,char** argv)
{
_CrtMemCheckpoint( &s1 );
char *str = NULL;
GetMemory(str, 100);
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
cout<<"Memory leak test!"<<endl;
_CrtDumpMemoryLeaks();
return 0;
}
有输出就说明有泄漏位置再s1和s2之间
LINUX mtrace
#include <mcheck.h>
#include <stdlib.h>
void main(void)
{
setenv("MALLOC_TRACE", "/home/l/mtrace_output", 1);
mtrace();
int *p1 = (int*)malloc(sizeof(int));
int *p2 = (int*)malloc(sizeof(int));
free(p1);
muntrace();
return;
}
gcc -g test_mtrace.c -o test_mtrace
./test_mtrace
mtrace test_mtrace mtrace_output