15.2 检测内存泄漏和其它错误

在理想状态下,当你的应用程序退出时,所有的对象都应该被你的应用程序或者wxWidgets本身释放,不会有残留的内存需要操作系统自己去释放.不用关心内存的释放听上去似乎非常的诱人,但是,我们还是要说,你应该自己控制所有对象的释放,而不要把它留给操作系统,因为通常,内存泄漏都是一些其它重大问题的前兆,它可能导致你的系统在一段时间内出现严重的内存问题.而当你已经转而关注程序的其它方面的时候,会过头来研究哪里出现了泄漏是一件非常令人沮丧的事情,因此,让你的程序尽量保持对内存泄漏的零容忍度不是一个坏事.

那么,怎样才可能让你的应用程序拥有这种检测内存泄漏的能力呢?各种各样的第三方工具提供了这种能力甚至更多其它的能力,而且, wxWidgets也提供了一个自己的简单的内建的内存检测器.如果你想使用它,在windows平台上,你需要打开setup.h中的几个开关,而在 linux平台上,configure程序需要一些特殊的开关:

在windows平台上,你需要:

#define wxUSE_DEBUG_CONTEXT 1
#define wxUSE_MEMORY_TRACING 1
#define wxUSE_GLOBAL_MEMORY_OPERATORS 1
#define wxUSE_DEBUG_NEW_ALWAYS 1

而对于configure程序,需要传递这样的参数:

--enable-debug --enable-mem_tracing --enable-debug_cntxt

另外,使用这个系统还有一个限制:到作者停笔之前,它还不支持MinGW或Cygwin编译器,并且如果你的代码中使用了STL或者使用CodeWarrior编译器,你将不能使用wxUSE_DEBUG_NEW_ALWAYS选项.

如果打开了wxUSEDEBUGNEWALWAYS选项,那么所有使用new操作分配对象的地方都将被new (_TFILE,__LINE)代替,后者已经被重新使用调试版本的定制内存分配和释放机制进行实现.如果想不重新定义new而显式使用调试版本的new过程,你可以在使用new的地方用WXDEBUG_NEW代替.

最简单的使用这个系统的方法是:什么都不做,就只运行你的程序,作该做的事情,然后退出应用程序,然后检查是否有任何内存泄漏被报告.下面的内存泄漏报告的一个例子:

There were memory leaks.

- Memory dump -
.\memcheck.cpp(89): wxBrush at 0xBE44B8, size 12
..\..\src\msw\brush.cpp(233): non-object data at 0xBE55A8, size 44
.\memcheck.cpp(90): wxBitmap at 0xBE5088, size 12
..\..\src\msw\bitmap.cpp(524): non-object data at 0xBE6FB8, size 52
.\memcheck.cpp(93): non-object data at 0xBB8410, size 1000
.\memcheck.cpp(95): non-object data at 0xBE6F58, size 4
.\memcheck.cpp(98): non-object data at 0xBE6EF8, size 8

- Memory statistics -
1 objects of class wxBitmap, total size 12
5 objects of class nonobject, total size 1108
1 objects of class wxBrush, total size 12

Number of object items: 2
Number of non-object items: 5
Total allocated size: 1132

上面的例子显示有一个wxBrush对象和一个wxBitmap对象被分配了但是没有被释放,还有一些其它的未知的对象没有被释放,因为它们没有提供wxWidgets的运行期类型信息,因此无法确定对象的类型.在某些集成开发环境中,你可以通过双击报告行显示相应的代码中分配这个对象的位置,这通常是检查内存泄漏问题一个很好的开始.当然,为了最好的报告效果,最好给任何继承自wxObject都提供运行期类型信息,方法是,在类声明的部分增加DECLARE_CLASS(class)宏,在类实现的地方增加IMPLEMENT_CLASS(class, parentClass)宏.

这个内存检测系统还试图检测那些内存越界或重复释放的错误.在分配内存的时候,它自动在已经分配的内存块上设置一个"good"标记, 在释放的时候将会检查这个标记,如果释放的内存块没有这个标记,则报告一个内存释放错误.这将帮助你发现那些隐藏的,也许在多次运行以后才会导致系统异常的内存错误.

wxDebugContext类的一些静态函数也很有用处,你可以通过PrintClasses函数获取当前系统中分配的对象的列表, PrintStatistics函数可以打印出当前系统中的已知类型对象和未知类型对象的个数.使用SetCheckpoint函数,你可以告诉 wxDebugContext忽略这个函数调用以前的内存分配动作,而仅关注这以后的内存分配.详情请参考samples/memcheck例子或者 wxDebugContext的相关手册.

除了wxWidgets内建的基本系统,你还可以使用别的商业的或者免费的内存检测软件,商业软件比如:BoundsChecker, Purify或AQtime等.自由软件比如:StackWalker,ValGrind,Electric Fence或来自Fluid Studios的MMGR等.如果你使用的是Visual C++,wxWidgets使用的是编译器内置的内存检测机制,它不会给出类的名字但是会给出行号.最好是打开 wxUSE_DEBUG_NEW_ALWAYS选项,因为它将重定义new过程,除非打开这个选项导致别的第三方的库出现兼容方面的问题.