一。何为内存泄漏
我们常说的内存泄漏通常指堆内存的泄漏,指我们的程序从堆中分配,使用完必须由程序显示地释放的内存。通常由程序中的malloc、realloc、new等函数分配的内存。在使用完后必须通过free/delete释放该内存块。如果该内存块不再访问却未被释放这就是我们通常所说的内存泄漏,而广义的内存泄漏不仅仅指堆内存的泄漏,还包括系统资源的申请释放,如socket及文件描述符、以及系统分配的对象。这种系统资源的泄漏可能比堆内存泄漏更严重。
二。内存分配
内存分配有以下几种方式
a.从静态存储区分配,内存在程序编译的时候就分配好,在程序的整个运行过程都存在。最常见的比如全局变量及静态变量。
b.栈上分配的内存,在函数执行过程中,函数内部变量在栈上创建,函数执行结束后这些存储单元会自动释放,栈内存分配内置在运算器的指令集中,运算效率高,但分配容量有限。
c.堆上分配的内存,即动态分配的内存。使用malloc/realloc/new申请的内存,由应用程序在使用完后,通过free/delete释放内存。此内存的生存期由应用程序指定。使用非常灵活(申请/释放由程序决定,申请内存的大小由程序指定),但也是最需要注意的地方,使用不当就会带来许多问题。
三。常见的内存泄漏
应用程序常见的内存泄漏会有下面几种:
经常发生。问题代码经常被执行到。每执行一次就会导致一块内存泄漏。带来后果就是程序占用存在不断持续增长。可能的情况不断分配内存,而未被释放,或者在程序退出时一次性释放,但程序是连续运行的,不及时释放内存会导致最终系统资源被耗尽。
偶然发生。只有在特定情况下才会发生。只有特定条件满足时问题代码才会被执行,导致一块内存泄漏。
一次性泄漏。问题代码只会被执行一次。
四。程序常见内存问题
内存错误不仅指内存泄漏。 解决内存错误比较麻烦,首先编译器不会自动发现内在问题。通常在程序运行期间才会出现。而且错误时隐时现,有些错误还很难再现,这都增加了问题改正的难度。
常见的内存问题有:
a.使用了未分配或分配未成功的内存。解决方法,在使用前检查指针是否为空,特别是在分配后。
b.使用了未初始化的内存。虽然分配成功,但未初始化
c.内存分配成功并且已经初始化,但操作越过了内存的边界。也就是通常所说的数组越界。
d.内存泄漏。就是上面所说的问题。
e.使用了已释放的内存。
避免内存问题的策略:
1。程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面;
2。函数的return注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时会被自动销毁。
3。使用free或delete释放了内存后,最好将指针设置为NULL。否则会导致产生“野指针”。
“野指针”也就是非法指针,指向垃圾的指针,一般由b,e情况引起的。
五。好的编程习惯
a.用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
b.不要忘记为数组和动态内存赋初值。防止使用未初始化的内存。
c.避免数组越界,特别要注意发生“多 1” 或者“少 1” 操作。
d.动态内存的申请与释放必须配对,防止内存泄漏。
e.free/delete释放了内存后,应将指针设置为NULL,防止产生“野指针”。
f.在未释放指向的内存前不要改变指针的指向,否则会带来内存泄漏。
另外可以使用内在检查工具对编译好的程序进行分析,检查程序是否有内存泄漏、及上面提到的内在错误。内存检查工具很多,比如我的前面帖子里介绍的linux系统下的valgrind。