我们所说的内存泄漏却实是指分配了但不释放的内存,但是称其为“内存泄漏”还有其它的特点:
1、指向它的指针已经指向了另一块区域(它被“遗忘”了)
2、或者,这段内存会不受控制地增长它的大小(它太“贪婪”)。
如果我写一个程序,分配一大段内存,然后就退出了它,没有释放内存,会有什么动作呢?
如果new是一个系统调用,那么就会在系统中留下一些“痕迹”,因为它使用了系统的handle。
如果new是一个封装后的调用,而它的基础_crt_heap已经不再了的话,会有什么影响?(系统handle已经关闭了)
也就是说,当在windows上写一个console程序(在其它操作系统上写以main()开始的程序时也同理)时,只要你使用库函数或new来分配内存,不论你分配多少,程序退出时就会被释放掉。
用一个笨方法证明:你可以写一个这样的程序,用ShellExecute运行它10000次,再看你的缓存文件+内存大小是不是等于分配内存的10000倍。
说句实话,如果内存泄漏不增长,它一点也不可怕。因为运行多少年它也是那么大,不会影响到正常使用。等到关机时管它还有没有内存“泄漏”呢!
为此,Scott Mayer还在他的书再版时专门加入了对他写的一段代码的解释,摘录如下(代码从略)
……
所有一切都很好,但从你皱起的眉头我可以知道你一定在担心内存泄漏。有着大量开发经验的你不会没注意到,airplane的operator new调用::operator new 得到了大块内存,但airplane的operator delete却没有释放它们。内存泄漏!内存泄漏!我分明听见了警钟在你脑海里回响。
但请仔细听我回答,这里没有内存泄漏!
引起内存泄漏的原因在于内存分配后指向内存的指针丢失了。如果没有垃圾处理或其他语言之外的机制,这些内存就不会被收回。但上面的设计没有内存泄漏,因为 它决不会出现内存指针丢失的情况。每个大内存块首先被分成airplane大小的小块,然后这些小块被放在自由链表上。当客户调用 airplane::operator new时,小块被自由链表移除,客户得到指向小块的指针。当客户调用operator delete时,小块被放回到自由链表上。采用这种设计,所有的内存块要不被airplane对象使用(这种情况下,是由客户来负责避免内存泄漏),要不 就在自由链表上(这种情况下内存块有指针)。所以说这里没有内存泄漏。
然而确实,::operator new返回的内存块是从来没有被airplane::operator delete释放,这个内存块有个名字,叫内存池。但内存泄漏和内存池有一个重要的不同之处。内存泄漏会无限地增长,即使客户循规蹈矩;而内存池的大小决 不会超过客户请求内存的最大值。
……
其实"内存泄漏"这个名词怎么解释是见仁见智的,
有的认为它不包含了内存丢失,像楼主就是这么认为,有的认为
它包含内存丢失,像微软。因此内存泄漏到底怎么解释,实际上
没有太大的指导意义。关键是要搞清楚怎样正确使用内存,其实
内存使用出错一般分为两种:1、是申请(new)并获得了内存但未
释放(delete),这种错误如果长时间、反复出现,最终内存会被
耗尽,但程序退出时会自动释放。2、是申请内存后,在使用的过
程中因为某些原因,致使内存管理(有可能是操作系统的,也有
可能是某个语言的具体实现,如VC的内存管理模块)用的数据出
错,这种情况发生后即使释放(delete)内存,也有可能无法释放
掉,这种情况就是楼主所谓的内存泄漏,内存管理用的数据都出
错了,因此什么情况都可能发生。
无论哪种情况,在特定的情况下,都会导致严重的后果,因此
都应该避免,因此基于这种考虑,我个人认为内存泄露包含内存丢
失似乎更好一些,毕竟MS的程序员们都经验丰富。
内存泄漏其实不只是new/delete,malloc/realloc/free等一堆造成的,还可能包括OLE的内存泄漏,特别是Com的应用程序, 由于都是公用OleAut32的,因此这个部分的内存泄漏是非常重要的,不知道OLE的内存泄漏BoundsCheck能否检查到呢?根据 Platform SDK的提示,那是非常麻烦的(先要创建CMallocSpy,然后需要使用对应checked版本的oleaut32.dll,还有在安装环境下配置, 我以前曾经在NT4.0作过,而且记得当时测试出VB6 Sp3的一个内存泄漏--sp5中已经修复了)。更有甚者,是设备的一些内存泄漏,比如Sockect,句柄没释放/线程异常产生的内存泄漏等。这些可能 产生的内存泄漏更多。