张志峰的博客

水滴石川,积少成多。

导航

Delphi的内存管理及内存泄露问题 FastMM4

Posted on 2016-11-10 13:45  ╰★张志峰★╮  阅读(3270)  评论(0编辑  收藏  举报

这几天因为一个程序长时间运行出现比较严重的内存泄露问题,开始关注了一下内存管理方面的东西,以前也注意内存管理,创建了对象及时释放,但总有忘了处理的情况。

在Delphi中没有自动回收机制,所以一定要及时释放使用的内存,虽然有时小的内存泄露并不会造成太大的问题。

Delphi中检测内存泄露可以使用开源的FastMM(http://sourceforge.net/projects/fastmm/),使用很简单,在工程的第一行引用FastMM4即可(注意,一定要在第一个Uses的位置),可以在调试程序时提示内存泄露情况,还可以生成报告。

在Delphi2010中,使用更加简单,只需要在工程开始的位置加上语句:

ReportMemoryLeaksOnShutdown := DebugHook<>0;

就可以了,并且在运行时不会出现提示。如果想要生成文件报告,还需要FastMM4,Delphi2010中或许有别的设置可以生成文件报告,没有找到。

可以修改FastMM4Options.inc中的参数开关来修改内存管理的相关设置。

后来在网上又发现了一个说是让Delphi支持自动垃圾回收的东东,很高兴,看了一下:

http://www.hpl.hp.com/personal/Hans_Boehm/gc/

有编译好的,可以支持多种开发语言,也可以支持Delphi,于是测试了一下,发现这个程序好像只是在系统退出时进行垃圾回收,比如你有两个窗体,form1和form2,form1调用form2,而form2中创建了一个对象没有释放,则在你注销form2的时候form2中使用的对象的内存并没有释放,而是要等到程序结束,所以这种垃圾回收时机有点晚了,如果你程序有大的内存泄露会导至系统内存问题的话,这个程序并不会帮你解决这个问题。(没有详细测试,哪位大侠有经验可以指点指点  :-)   )

所以,最好的办法还是你自己在写程序的时候一定要注意释放掉使用的内存,可以借助fastmm来帮你找可能的内存泄露。

 

附:FastMM的使用(转自网络)

 使用说明如下:     
  1.打开FastMM4的调试功能,首先在自己的project里把FastMM4放在最前面,例如:   
      FastMM4,   
      Main   in   ‘Main.pas’   {MainForm},   
    
  再修改FastMM4Options.inc,打开全调试模式。例:   
  {$define   FullDebugMode}   
  也可以在project中定义编译常量:FullDebugMode。同时把FastMM_FullDebugMode.dll拷贝到编译后生成的可执行程序所在目录。   
  再要打开内存泄漏报告:EnableMemoryLeakReporting。一般情况下是缺省打开的。   
  这样就打开了全调试模式,如果发生内存泄漏将会生成报告文件,如果在IDE运行的时候还会弹出一个对话框显示。报告文件类似:XXX_MemoryManager_EventLog.txt   
    
  2.报告文件由两部分组成,并且是每次运行append。   
    
  第一部分是泄漏的详细内容,将每个没释放的内存块详细信息显示出来。例:   
  A   memory   block   has   been   leaked.   The   size   is:   28       {一个28字节的内存块在程序结束后没有被释放}   
    
  {这个内存块在分配的时候的调用堆栈,也就是Call   Stack,可以清楚看出调用函数的次序。如果是系统dll则还有相应的函数名。}   
  Stack   trace   of   when   this   block   was   allocated   (return   addresses):   
  4028E7   
  4030EC   
  406649   
  412365   
  41236E   
  411DD3   
  426B45   
  427236   
  42888C   
  {这个内存类型,如果是字符串string或TObject继承的对象则会显示名称及行号。}   
  The   block   is   currently   used   for   an   object   of   class:   Unknown   
  {将内存块头256个字符显示出现,作为内容提示。}   
  Current   memory   dump   of   256   bytes   starting   at   pointer   address   107BDD8:   
    
  第二部分是总结性内容,例:   
    
  {这个小型内存块泄漏的报告,如果有大型内存块泄漏则会加一行专门提示大型内存块泄漏。}   
  This   application   has   leaked   memory.   The   small   block   leaks   are   (excluding   expected   leaks   registered   by   pointer):   
    
  {21-28字节的内存块泄漏,未知类型一个}   
  21   -   28   bytes:   Unknown   x   1   
    
  Note:   Memory   leak   detail   is   logged   to   a   text   file   in   the   same   folder   as   this   application.   To   disable   this   memory   leak   check,   undefine   “EnableMemoryLeakReporting”.     
    
  有了这份报告只不过了解到内存泄漏存在,但是哪里没释放就还需要更进一步地调查。   
    
  调查的目标有:   
    
  1.内存块分配在哪个函数里哪段代码。   
    
  这个在报告里可以结合内容和调用堆栈来看。前256个字节可以进行分析,推测分配者,调用堆栈就直接指出了分配函数,不过是一些地址,不能直接知道函数名和代码段。这时候就需要在delphi   ide环境下查看二进制内存映像了,就是View   CPU功能。   
    
  在设定断点并停下后,可以View   CPU,在菜单View=>Debug   Window=>CPU     快捷键:Ctrl+Alt+C   
    
  View   CPU   Window:       
    
  正中就是内存映像,而且源码也相应地标注好了,左边列的地址就是内存报告中的Call   Stack中的地址,翻页找到所对应的代码就知道哪里分配内存了。   
    
  2.检查释放内存的地方是否被调用,可以用日志或断点来调试,如果压根就没有释放内存那就补上代码,如果有却没有执行则检查一下执行条件是否正确,如果断点没起作用很可能是因为代码永远不会被执行(死代码)。   
    
  这要靠经验和调试,基本上借助IDE和内存报告就可以很好地防止内存泄漏。同时要加强测试用例,争取在测试用例中能遍历到所有的代码和大部分关键功能,这样内存泄漏报告就会更准确一点。   
    
  fastmm每次在程序关闭后就会根据情况生成内存泄漏报告,如果没有弹出内存泄漏警告则恭喜你,内存管得很好。   
    
  另:   
    
  1.内存管理不是GC自动回收内存,而是检查是否有泄漏。   
    
  2.windows系统的内存泄漏是无法检查的,仅限于应用程序内部,不过检查出系统泄漏也没办法,只能等更新了。   
    
  3.检查泄漏后要自己去检查代码补齐内存释放,报告并不能做这事