珍惜每一滴水(kbmmw 中的内存调试)

简介: 作为一个服务器端的应用,最基本的要求就是稳定,当然要做一个稳定的服务器端,需要涉及到很多方面, 内存泄露就是稳定的一个致命杀手,因为服务器的物理内存是有限的,即使一个功能有很小的内存泄露,经过 长时间的运行,也会累积成一个非常大的内存泄露,导致服务器内存耗尽,系统崩溃。

    作为一个服务器端的应用,最基本的要求就是稳定,当然要做一个稳定的服务器端,需要涉及到很多方面,

内存泄露就是稳定的一个致命杀手,因为服务器的物理内存是有限的,即使一个功能有很小的内存泄露,经过

长时间的运行,也会累积成一个非常大的内存泄露,导致服务器内存耗尽,系统崩溃。因此珍惜服务器资源是

开发者必须重视的(当然了,对于内存无法管理的语言及框架,那就算了)。

   最新版的kbmmw 中加入了内存调试功能,这个功能不但可以应用在kbmmw 服务器中,你也可以在其他程序中

使用。

  其实自从delphi 2007 开始,delphi 就默认使用FastMM 作为内存管理器,FastMM 也自带了内存泄露报告功能。

但是FastMM只能追踪通过GetMem 分配的内存资源,无法追踪windows其他方式分配的内存(Virtual/Heap/Global/Local).

 另外,即使你屏蔽了FastMM 的内存泄露报告,kbmmw 的内存调试器可以在应用中的任何时间来记录内存的使用情况。

我们要使用kbmmw 的内存调试功能,先做一下准备工作。

首先我们要打开 kbmmwconfig.inc 加入


{$DEFINE KBMMW_SUPPORT_DEBUGMEMORY}
{$DEFINE KBMMW_INSTALL_DEBUGMEMORY_HANDLERS}

这两句条件编译,以便激活kbmmw 的内存调试功能,默认是关闭的,如图。

如果没有 {$DEFINE KBMMW_SUPPORT_DEBUGMEMORY} 这一句,内存调试不起作用。

如果没有 {$DEFINE KBMMW_INSTALL_DEBUGMEMORY_HANDLERS},内存追踪不起作用。

 

现在就可以在你的程序里面 加入kbmMWDebugMemory 单元了,你的程序就有了内存调试功能了。

 

kbmmw 自动的勾住了 delphi 程序中所有的内存分配功能,包括以前的borlandMM, 和windows 自己的Virtualxxx, Globalxxx,

Localxxx and Heapxxx 这些函数。当然了,也完美支持Fastmm 的内存管理器。

kbmmw 为每次的内存分配和内存再分配都用一个唯一的自增64位 数来标记。通过这个数字,我们可以追踪在某个时间段中内存分配

的情况,这些点叫checkpoints.原理上,一个checkpoint 就是一个64位的数字,通过这些数字我们可以追踪每次的内存分配情况。

kbmmw 同时维护着一个分配的内存及分配次数的统计表。你可以任何时候来查阅这个统计表

 

procedure TForm1.Timer1Timer(Sender: TObject);
begin
     lLiveAllocationsCount.Caption:=inttostr(TkbmMWDebugMemory.LiveAllocationCount)+' ('+inttostr(TkbmMWDebugMemory.LiveAllocationCountPerSec)+'/sec)';
     lLiveAllocSize.Caption:=inttostr(TkbmMWDebugMemory.LiveAllocationSize)+' ('+inttostr(TkbmMWDebugMemory.LiveAllocationSizePerSec)+'/sec)';
     lMaxAllocationCount.Caption:=inttostr(TkbmMWDebugMemory.MaxAllocationCount);
     lMaxAllocationSize.Caption:=inttostr(TkbmMWDebugMemory.MaxAllocationSize);
     lMaxCapacity.Caption:=inttostr(TkbmMWDebugMemory.CurrentAllocationCountCapacity);
     lCheckPoint.Caption:=inttostr(FCheckPoint);
     lGCCount.Caption:=inttostr(TkbmMWDebugMemory.AllocationKeys.GCCount);
end;

 

 其中:

   LiveAllocationCount 是当然使用的、活动着的内存。它包括所有的 (Borland- object/string/memory, Localmemory, Globalmemory, Virtualmemory, Heapmemory).

  其它参数的作用请参考kbmmw 的源代码(没有源代码?那就买一份呗)。

知道了内存分配情况,那么接下来就是如何在程序结束后检测内存泄露了。

什么时候内存泄露?

就是被分配的内存,永远没有被释放。

有些内存泄露无所谓,因为其在整个应用过程中只发生一次,有点像定义了一个全局变量,这种泄漏时是可预测的,而且不随时间的增长而增长,这类泄露在delphi

的RTL 和VCL 中有很多,我们无法消灭它们,也没必要消灭它们(对于爱干、鱼儿的这些强迫症患者简直就是噩梦)。

对于那些每次执行都发生的内存泄露,随着时间的增长越来越多,耗尽系统资源,到最后导致系统崩溃。因此这种泄露我们必须消灭。

因为内存追踪无法了解一个人(我不是指“竹子”)的想法,因此在程序运行过程中,无法确定一块主动分配的内存是否真的需要释放?因此只有在程序退出时,

我们才能知道这些内存确实需要释放,然而并没有释放。所以只有在程序退出时,才能确定内存是否泄漏?

在kbmmw 里面,我们可以在尽可能早的地方把内存检测加入。

TkbmMWDebugMemory.ReportDestination('c:\temp\leaks.txt'); 
TkbmMWDebugMemory.ReportLeaksOnShutdown:=true; 
TkbmMWDebugMemory.StartLeakChecking; 

我们可以做一个明显的泄漏,测试一下

procedure TForm1.Button8Click(Sender: TObject);
var
   sl:Tstringlist;
begin

  sl:=Tstringlist.Create;
end;

运行程序,执行以上代码,然后退出。

就会显示以下提示:

如果我们去看leaks.txt

就会很明显的发现这个问题。

以上信息有时很精确,有时不一定准确,主要依靠你程序编译时的一些设置,尤其是优化代码很厉害的话,会差异很大。

当然了,内存使用情况和调试涉及的东西很多,如果你要进一步深入的话,可以看kbmmw 的源码和例子。

 

有一点需要说明,以上所有的操作都是有代价的,如果在正式生产环境中使用,尽量不要启用内存调试功能。

只有出现内存泄露时,才建议使用以上功能发现泄露原因,消灭掉泄漏后,立即关闭调试功能。

 

----------------

本来以为完了,鱼儿和竹子追着问如何知道出现发生泄漏的源代码行号,其实kbmmw 里面完全可以实现。

只是需要在编译时增加相关的调试信息。因为没有足够的调试信息,神仙也不知道是源代码的哪一行发生泄露了。

请如图设置。

 

同时,我们在程序里面需要增加相应的处理代码

procedure TForm1.FormCreate(Sender: TObject);
begin

      TkbmMWDebugStackTrace.Initialize;
     TkbmMWDebugMemory.ReportDestination('c:\temp\leaks.txt');
     TkbmMWDebugMemory.ReportLeaksOnShutdown:=true;
      TkbmMWDebugMemory.CollectStacks:=True;
     TkbmMWDebugMemory.StartLeakChecking;
     FCheckPoint:=TkbmMWDebugMemory.CheckPoint;


end;

 

这样再运行。

先记住我们故意出泄露的地方。

 

再看看泄露文件

 

现在可以消停了吧。

 

目录
相关文章
|
5月前
|
存储 编译器 C语言
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存2
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
|
5月前
|
程序员 C语言 C++
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存1
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
|
11月前
|
Java Linux 程序员
Linux平台中调试C/C++内存泄漏方法 (腾讯和MTK面试的时候问到的)
Linux平台中调试C/C++内存泄漏方法 (腾讯和MTK面试的时候问到的)
|
3月前
|
运维 Java Linux
(九)JVM成神路之性能调优、GC调试、各内存区、Linux参数大全及实用小技巧
本章节主要用于补齐之前GC篇章以及JVM运行时数据区的一些JVM参数,更多的作用也可以看作是JVM的参数列表大全。对于开发者而言,能够控制JVM的部分也就只有启动参数了,同时,对于JVM的性能调优而言,JVM的参数也是基础。
|
2月前
|
监控 算法 iOS开发
Xcode调试内存最新理解
【8月更文挑战第12天】在Xcode中进行内存调试对确保iOS应用的稳定性和性能至关重要。主要利用Instruments套件内的工具如Leaks检测内存泄漏,Allocations追踪内存分配详情,及Memory Graph Debugger提供直观的内存结构视图。Xcode还支持内存图视图以了解对象布局与引用,动态内存分析实现实时监控,及符号化调试信息帮助定位问题源代码。最佳实践包括遵循内存管理原则、定期调试、优化代码逻辑,以及学习优秀代码案例。通过这些技术和策略,开发者能有效管理和优化应用的内存使用。
|
5月前
|
存储 定位技术 开发工具
Android 开发前的设计,Android之内存泄漏调试学习与总结
Android 开发前的设计,Android之内存泄漏调试学习与总结
|
5月前
|
缓存 Linux iOS开发
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
537 1
|
5月前
|
监控 NoSQL Unix
内存泄漏专题(6)AIX系统内存泄漏调试浅探
内存泄漏专题(6)AIX系统内存泄漏调试浅探
84 0
|
5月前
|
XML NoSQL Linux
内存泄漏专题(3)内存泄漏调试神器valgrind
内存泄漏专题(3)内存泄漏调试神器valgrind
84 0
|
缓存 API 数据安全/隐私保护
10.3 调试事件转存进程内存
我们继续延申调试事件的话题,实现进程转存功能,进程转储功能是指通过调试API使获得了目标进程控制权的进程,将目标进程的内存中的数据完整地转存到本地磁盘上,对于加壳软件,通常会通过加密、压缩等手段来保护其代码和数据,使其不易被分析。在这种情况下,通过进程转储功能,可以将加壳程序的内存镜像完整地保存到本地,以便进行后续的分析。在实现进程转储功能时,主要使用调试API和内存读写函数。具体实现方法包括:以调试方式启动目标进程,将其暂停在运行前的位置;让目标进程进入运行状态;使用ReadProcessMemory函数读取目标进程内存,并将结果保存到缓冲区;将缓冲区中的数据写入文件;关闭目标进程的调试状态
10.3 调试事件转存进程内存