Debugging memory usage with kbmMW

时间:2022-09-02 00:13:22

kbmMW的更高版本包含越来越多的功能,可用于通常的日志记录,审计,运行期发生异常时的堆栈跟踪以及现在的内存使用调试。
这些功能实际上可用于任何应用程序,不仅限于基于kbmMW开发的多层应用系统。
我已经在kbmMW上写了一些关于日志记录和审计系统的文章,其中还介绍了如何在处理异常时的堆栈跟踪,最近新增加的功能是能够实时跟踪应用程序的每个内存分配。
你可能会问,FastMM已经内置了内存泄漏检测,为什么还用kbmMW的内存调试器呢?
FastMM仅跟踪通过常规GetMem等完成的内存分配,它不跟踪通过Windows中可用的任何Virtual / Heap / Global / Local分配方法进行的分配。
即使禁用了FastMMs内存泄漏检测,kbmMW的调试器仍然可以工作,并提供在应用程序中随时记录内存使用和分配的功能。

开始

您需要将kbmMWDebugMemory添加到应用程序的uses子句中,并且必须确保在kbmMWConfig.inc文件中设置了以下定义:

{$DEFINE KBMMW_SUPPORT_DEBUGMEMORY}
{$DEFINE KBMMW_INSTALL_DEBUGMEMORY_HANDLERS}

否则,所有的内存调试将被禁用。

如果不定义KBMMW_SUPPORT_DEBUGMEMORY,则没有可用的内存调试功能(包括所有函数/方法)。

如果不定义KBMMW_INSTALL_DEBUGMEMORY_HANDLERS,那么内存调试系统将不会自动安装钩子和处理程序,可以使用这些函数(参见上文),但是不执行内存跟踪。

设置两个定义后,将kbmMWDebugMemory单元文件添加到应用程序或单元的uses子句中,将自动安装kbmMW的内存调试功能和挂钩(hooks)。

为了充分利用它,还应该确保应用程序是使用debug方式编译,编译时要设置Project/Options/Compiler/Stack frames为True,同时生成*.tds文件或者Detailed Map文件。 如果要在IDE外部运行应用程序并需要内存调试,则生成的* .tds或* .map文件必须与可执行文件位于同一目录。

下图为如何设置项目的编译参数:

Debugging memory usage with kbmMW

Debugging memory usage with kbmMW

概念

kbmMW自动挂钩(hooks)Borland类型的内存分配方法以及Microsoft Windows Virtualxxx,Globalxxx,Localxxx和Heapxxx分配方法。 它还与包括FastMM在内的任何第三方内存管理器很好地配合使用。

每次分配内存和重新分配,kbmMW都会为其分配一个唯一的递增64bit数字(uint64)。 此数字可用于跟踪两个时间点之间的所有内存分配,称之为检查点。
基本上检查点就是一个64bit的数字(uint64), 通过这种方式,您可以准确地检查一段代码中发生了什么内存分配,甚至可以获得内存分配发生位置的堆栈跟踪信息。

统计

kbmMW维护已分配内存和分配计数的统计信息。

这些可以在任何时候获得,如下所示:

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);

LiveAllocationCount是检测到的活动的,正在使用的内存分配数。 它计算所有类型的内存分配(Borland - 对象/字符串/内存,本地内存,全局内存,虚拟内存,堆内存)。 有些情况造成计数不准确,例如FastMM将通过VirtualAlloc分配大块内存,并使用GetMem(Borlands内存管理器接口)将这些内存分发给应用程序,这时候就将看到一个不精确的计数(和大小),因为计数将同时包括由FastMM调用VirtualAlloc产生的,以及由应用程序进行的各个GetMem(等)产生的。

因此,实时值可用来比较,但不是精确值。 您可以依赖它们来显示例如增加内存使用(可能表示泄漏),但您不能直接依赖于确切的绝对值,因为由于上述情况,某些分配会被计算两次。

在关闭应用时检测泄漏

什么是泄漏? 就是已分配的资源,从未取消分配。

有些泄漏是坏的,有些则不是。 那些不坏的,是由于单个非重复分配而发生的泄漏,通常在应用程序启动期间进行,从未明确释放。 事实上,RTL和VCL包含许多这样的泄漏。

这些泄漏并不错,因为操作系统(在这种情况下为Windows)将在应用程序关闭时自动释放应用程序使用的所有内存。

糟糕的漏洞是那些反复分配更多内存,每次分配都不会释放它。 这些泄漏最终会使应用程序耗尽内存空间或使系统因物理RAM耗尽而导致速度极慢的内存分页。 内存分页是当前不太重要的内存段写入磁盘的地方,为当前正在分配或从磁盘(页面文件)读入的当前更多导入的内存段腾出空间。

操作系统发生某些分页是正常的,但是如果应用程序分配太多内存,造成OS执行内存分页,以使所有其他进程显着减慢,这就不正常的了,表明发生了严重的内存泄漏。

在应用程序关闭时,是唯一能够合理、可靠地检测是否发生了内存泄漏的最好时间点。 为什么? 因为当时您知道应用程序析构函数/ FormClose事件等已经释放了所有(大多数)已分配的内存。

kbmMW使这很容易检查。 先在应用程序启动早期的某些地方,例如在Form.OnCreate事件中,写如下代码:

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

现在已经定义了泄漏报告文件的存放位置,并希望在应用关闭时创建泄漏检测报告,同时希望立即启动泄漏检测。 事实上,StartLeakChecking所做的就是加载TDS文件或MAP调试信息文件(用于堆栈跟踪)然后
注册一个基线检查点,至此开始检查内存分配是否已被释放。 此时间点之前的所有内存分配都将被忽略,因此所有分配都构建在VCL / RTL泄漏中,并且所有对象都分配给TDS / MAP信息。

您可以通过检查以下内容来实际查看基线值:

ShowMessage(‘Baseline=’+inttostr(TkbmMWDebugMemory.Baseline)); 

如果您希望泄漏检测包括自安装内存分配挂钩以来的所有内容。 将基线设置为零。 例如。

TkbmMWDebugMemory.Baseline:=; 

最后分配的检查点可以通过以下方式获得:

var
cp:TkbmMWDebugMemoryAllocationKey;
...
cp:=TkbmMWDebugMemory.Checkpoint;
ShowMessage('Checkpoint='+inttostr(key));

现在,当您运行应用程序然后关闭它时,kbmMW将生成一个内存泄漏报告(对象没释放)。

默认情况下,它将在屏幕上显示概要信息,如下所示:

Debugging memory usage with kbmMW

并在指定文件中输出详细信息:

Debugging memory usage with kbmMW

正如所见,调试器能够确定其对象,字符串或其他类型的内存是否已泄露,并在文件的第一部分中提供有关特定类实例的泄漏数量的统计信息。 在这种情况下,只是TkbmMWInnerThread的一个实例。 这是一个安全漏洞,它只是存在,因为kbmMW调度程序有一个运行处理事件的宽松事件线程。 内存调试器已注册计划事件以计算分配/秒。

堆栈跟踪信息或多或少,不一定精确,这取决于编译到应用程序中的调试信息量(必须启用堆栈帧,即Project/Options/Compiler/Stack frames设置为True,这也是默认设置),并且取决于您是否让Delphi生成kbmMW的堆栈跟踪功能可以使用的外部TDS文件或MAP文件。

您可以选择不收集内存分配的堆栈跟踪信息,这将节省一些内存和CPU时间,并使您的报告更简洁。 这可以通过在StartLeakChecking被调用之前添加以下行来完成:

TkbmMWDebugMemory.CollectStacks:=false; 

在特定情况下跟踪分配

您可能希望报告在特定时间间隔内或代码中两个位置之间进行的所有分配。 您可以使用检查点方法在两个位置获取数字,然后在报告中选择所需内容。 例如:

FMyCP1,FMyCP2: TkbmMWDebugMemoryAllocationKey;
...
FMyCP1:=TkbmMWDebugMemory.Checkpoint;
<your interesting code>
FMYCP2:= TkbmMWDebugMemory.Checkpoint;
MyReport(FMyCP1,FMyCP2);
...
procedure MyReport(ACP1,ACP2: TkbmMWDebugMemoryAllocationKey);
var
sr:TkbmMWDebugMemoryScanResult;
begin
sr:=TkbmMWDebugMemoryScanResult.Create;
try
TkbmMWDebugMemory.Scan(sr,FMyCP1,FMyCP2,[mwdmstObject]);
sr.Log(mwltDebug,mwllDetailed,[mwsrpoNoStack]);
finally
sr.Free;
end;
end;

上面的示例,仅报告对象实例的内存分配,并通过kbmMW日志系统作为调试日志报告它们,同时,不记录堆栈跟踪信息。
以上是对kbmMW的内存调试器的一个简要介绍,希望你能掌握其用法,快速解决遇到的内存泄漏问题。

Debugging memory usage with kbmMW的更多相关文章

  1. Memory usage of a Java process java Xms Xmx Xmn

    http://www.oracle.com/technetwork/java/javase/memleaks-137499.html 3.1 Meaning of OutOfMemoryError O ...

  2. Shell script for logging cpu and memory usage of a Linux process

    Shell script for logging cpu and memory usage of a Linux process http://www.unix.com/shell-programmi ...

  3. 5 commands to check memory usage on Linux

    Memory Usage On linux, there are commands for almost everything, because the gui might not be always ...

  4. SHELL&colon;Find Memory Usage In Linux &lpar;统计每个程序内存使用情况&rpar;

    转载一个shell统计linux系统中每个程序的内存使用情况,因为内存结构非常复杂,不一定100%精确,此shell可以在Ghub上下载. [root@db231 ~]# ./memstat.sh P ...

  5. Why does the memory usage increase when I redeploy a web application&quest;

    That is because your web application has a memory leak. A common issue are "PermGen" memor ...

  6. Reducing and Profiling GPU Memory Usage in Keras with TensorFlow Backend

    keras 自适应分配显存 & 清理不用的变量释放 GPU 显存 Intro Are you running out of GPU memory when using keras or ten ...

  7. GPU Memory Usage占满而GPU-Util却为0的调试

    最近使用github上的一个开源项目训练基于CNN的翻译模型,使用THEANO_FLAGS='floatX=float32,device=gpu2,lib.cnmem=1' python run_nn ...

  8. 【转】C&plus;&plus; Incorrect Memory Usage and Corrupted Memory&lpar;模拟C&plus;&plus;程序内存使用崩溃问题&rpar;

    http://www.bogotobogo.com/cplusplus/CppCrashDebuggingMemoryLeak.php Incorrect Memory Usage and Corru ...

  9. Redis&colon; Reducing Memory Usage

    High Level Tips for Redis Most of Stream-Framework's users start out with Redis and eventually move ...

随机推荐

  1. 如何同时打开两个excel

    1. 打开一个excel1 2. 不要双击想要打开的excel2.右键excel应用的图标,选择excel2007. 3. 将excel2拖动到2所打开的新建excel中. 4. over.

  2. &lpar;原创&rpar;项目部署-Tomcat设置默认访问项目及项目重复加载问题处理

    主要是通过配置<Tomcat安装目录>/conf/server.xml文件 步骤: 1.打开server.xml,在</Host>的上一行添加内容格式如下 <Contex ...

  3. java 多线程6(线程的&&num;183&semi;通讯)

    问题1: 为什么wait() 和 notify()是Object类中的方法,而不是Thread类中的方法呢? 答:因为锁是任意对象的所以要在Object类中,如果在Thread类中锁对象不是任意的了. ...

  4. Spark RDD&sol;Core 编程 API入门系列之动手实战和调试Spark文件操作、动手实战操作搜狗日志文件、搜狗日志文件深入实战(二)

    1.动手实战和调试Spark文件操作 这里,我以指定executor-memory参数的方式,启动spark-shell. 启动hadoop集群 spark@SparkSingleNode:/usr/ ...

  5. JVM中可生成的最大Thread数量

    最近想测试下Openfire下的最大并发数,需要开大量线程来模拟客户端.对于一个JVM实例到底能开多少个线程一直心存疑惑,所以打算实际测试下,简单google了把,找到影响线程数量的因素有下面几个: ...

  6. NSArray的4种遍历方式

    前言:NSArray对应的是java的List,不同的是其元素不能更改,不过其派生类NSMutableArray可以更改,遍历的方式跟java的List基本一样 一.  for循环 Student * ...

  7. C&num;-TextBox-登录表单password无形---ShinePans

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. scala练手之数字转汉字小工具

    输入数字,转换成汉字,在统计数据量时很好用,而输入数字转成大写汉字,可以用于填写收据报销单哦 下载链接 https://pan.baidu.com/s/1nv3Ci6l 效果图如下: 直接上代码 ob ...

  9. 1c19b35b005744d55261682b361804fa 这个是MD5

    1c19b35b005744d55261682b361804fa   这个是MD51c19b35b005744d55261682b361804fa   这个是MD51c19b35b005744d552 ...

  10. NFS 安装与配置

    NFS通常用于网络中的多台计算机实现共享存储. 由于测试环境没有购买阿里云的NFS,所以自己搭建一个NFS文件系统,实现一些比如上传图片,静态资源等同享功能. 下面的测试是在CentOS releas ...