使用BoundsChecker检测内存泄漏

 

http://www.51testing.com/?65703/action_viewspace_itemid_65101.html

使用BoundsChecker检测内存泄漏:

   BoundsChecker采用一种被称为 Code Injection的技术,来截获对分配内存和释放内存的函数的调用。简单地说,当你的程序开始运行时,BoundsChecker的DLL被自动载入进程的地址空间(这可以通过system-level的Hook实现),然后它会修改进程中对内存分配和释放的函数调用,让这些调用首先转入它的代码,然后再执行原来的代码。BoundsChecker在做这些动作的时,无须修改被调试程序的源代码或工程配置文件,这使得使用它非常的简便、直接。

   这里我们以malloc函数为例,截获其他的函数方法与此类似。

   需要被截获的函数可能在DLL中,也可能在程序的代码里。比如,如果静态连结C-Runtime Library,那么malloc函数的代码会被连结到程序里。为了截获住对这类函数的调用,BoundsChecker会动态修改这些函数的指令。

   以下两段汇编代码,一段没有BoundsChecker介入,另一段则有BoundsChecker的介入:

   126: _CRTIMP void * __cdecl malloc (

   127: size_t nSize

   128: )

   129: {

   00403C10 push ebp

   00403C11 mov ebp,esp

   130: return _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);

   00403C13 push 0

   00403C15 push 0

   00403C17 push 1

   00403C19 mov eax,[__newmode (0042376c)]

   00403C1E push eax

   00403C1F mov ecx,dword ptr [nSize]

   00403C22 push ecx

   00403C23 call _nh_malloc_dbg (00403c80)

   00403C28 add esp,14h

   131: }

   以下这一段代码有BoundsChecker介入:

   126: _CRTIMP void * __cdecl malloc (

   127: size_t nSize

   128: )

   129: {

   00403C10 jmp 01F41EC8

   00403C15 push 0

   00403C17 push 1

   00403C19 mov eax,[__newmode (0042376c)]
  
   00403C1E push eax

   00403C1F mov ecx,dword ptr [nSize]

   00403C22 push ecx

   00403C23 call _nh_malloc_dbg (00403c80)

   00403C28 add esp,14h

   131: }

   当BoundsChecker介入后,函数malloc的前三条汇编指令被替换成一条jmp指令,原来的三条指令被搬到地址01F41EC8处了。当程序进入malloc后先jmp到01F41EC8,执行原来的三条指令,然后就是BoundsChecker的天下了。大致上它会先记录函数的返回地址(函数的返回地址在stack上,所以很容易修改),然后把返回地址指向属于BoundsChecker的代码,接着跳到malloc函数原来的指令,也就是在00403c15的地方。当malloc函数结束的时候,由于返回地址被修改,它会返回到BoundsChecker的代码中,此时BoundsChecker会记录由malloc分配的内存的指针,然后再跳转到到原来的返回地址去。

   如果内存分配/释放函数在DLL中,BoundsChecker则采用另一种方法来截获对这些函数的调用。BoundsChecker通过修改程序的DLL Import Table让table中的函数地址指向自己的地址,以达到截获的目的。关于如何拦截Windows的系统函数,《程序员》杂志2002年8期,《API钩子揭密(下)》,对修改导入地址表做了概要的描述。我就不再赘述。

   截获住这些分配和释放函数,BoundsChecker就能记录被分配的内存或资源的生命周期。接下来的问题是如何与源代码相关,也就是说当BoundsChecker检测到内存泄漏,它如何报告这块内存块是哪段代码分配的。答案是调试信息(Debug Information)。当我们编译一个Debug版的程序时,编译器会把源代码和二进制代码之间的对应关系记录下来,放到一个单独的文件里(.pdb)或者直接连结进目标程序中。有了这些信息,调试器才能完成断点设置,单步执行,查看变量等功能。BoundsChecker支持多种调试信息格式,它通过直接读取调试信息就能得到分配某块内存的源代码在哪个文件,哪一行上。使用Code Injection和Debug Information,使BoundsChecker不但能记录呼叫分配函数的源代码的位置,而且还能记录分配时的Call Stack,以及Call Stack上的函数的源代码位置。这在使用像MFC这样的类库时非常有用,以下我用一个例子来说明:

void ShowXItemMenu()

   {

   …

   CMenu menu;

   menu.CreatePopupMenu();

   //add menu items.

   menu.TrackPropupMenu();

   …

   }

   void ShowYItemMenu( )

   {

   …

   CMenu menu;

   menu.CreatePopupMenu();

   //add menu items.

   menu.TrackPropupMenu();

   menu.Detach();//this will cause HMENU leak

   …

   }

   BOOL CMenu::CreatePopupMenu()

   {


   hMenu = CreatePopupMenu();

   …

   }

   当调用ShowYItemMenu()时,我们故意造成HMENU的泄漏。但是,对于BoundsChecker来说被泄漏的HMENU是在class CMenu::CreatePopupMenu()中分配的。假设的你的程序有许多地方使用了CMenu的CreatePopupMenu()函数,如果只是告诉你泄漏是由CMenu::CreatePopupMenu()造成的,你依然无法确认问题的根结到底在哪里,在ShowXItemMenu()中还是在ShowYItemMenu()中,或者还有其它的地方也使用了CreatePopupMenu()?有了Call Stack的信息,问题就容易了。BoundsChecker会如下报告泄漏的HMENU的信息:

Function

File

Line

CMenu::CreatePopupMenu

E:\8168\vc98\mfc\mfc\include\afxwin1.inl

1009

ShowYItemMenu

E:\testmemleak\mytest.cpp

100

这里省略了其他的函数调用


   如此,我们很容易找到发生问题的函数是ShowYItemMenu()。当使用MFC之类的类库编程时,大部分的API调用都被封装在类库的class里,有了Call Stack信息,我们就可以非常容易的追踪到真正发生泄漏的代码。

   记录Call Stack信息会使程序的运行变得非常慢,因此默认情况下BoundsChecker不会记录Call Stack信息。可以按照以下的步骤打开记录Call Stack信息的选项开关:

   1. 打开菜单:BoundsChecker|Setting…

   2. 在Error Detection页中,在Error Detection Scheme的List中选择Custom

   3. 在Category的Combox中选择 Pointer and leak error check

   4. 钩上Report Call Stack复选框

   5. 点击Ok

基于Code Injection,BoundsChecker还提供了API Parameter的校验功能,memory over run等功能。这些功能对于程序的开发都非常有益。由于这些内容不属于本文的主题,所以不在此详述了。

   尽管BoundsChecker的功能如此强大,但是面对隐式内存泄漏仍然显得苍白无力。所以接下来我们看看如何用Performance Monitor检测内存泄漏。

   使用Performance Monitor检测内存泄漏

   NT的内核在设计过程中已经加入了系统监视功能,比如CPU的使用率,内存的使用情况,I/O操作的频繁度等都作为一个个Counter,应用程序可以通过读取这些Counter了解整个系统的或者某个进程的运行状况。Performance Monitor就是这样一个应用程序。

   为了检测内存泄漏,我们一般可以监视Process对象的Handle Count,Virutal Bytes 和Working Set三个Counter。Handle Count记录了进程当前打开的HANDLE的个数,监视这个Counter有助于我们发现程序是否有Handle泄漏;Virtual Bytes记录了该进程当前在虚地址空间上使用的虚拟内存的大小,NT的内存分配采用了两步走的方法,首先,在虚地址空间上保留一段空间,这时操作系统并没有分配物理内存,只是保留了一段地址。然后,再提交这段空间,这时操作系统才会分配物理内存。所以,Virtual Bytes一般总大于程序的Working Set。监视Virutal Bytes可以帮助我们发现一些系统底层的问题; Working Set记录了操作系统为进程已提交的内存的总量,这个值和程序申请的内存总量存在密切的关系,如果程序存在内存的泄漏这个值会持续增加,但是Virtual Bytes却是跳跃式增加的。

   监视这些Counter可以让我们了解进程使用内存的情况,如果发生了泄漏,即使是隐式内存泄漏,这些Counter的值也会持续增加。但是,我们知道有问题却不知道哪里有问题,所以一般使用Performance Monitor来验证是否有内存泄漏,而使用BoundsChecker来找到和解决问题

   当Performance Monitor显示有内存泄漏,而BoundsChecker却无法检测到,这时有两种可能:第一种,发生了偶发性内存泄漏。这时你要确保使用Performance Monitor和使用BoundsChecker时,程序的运行环境和操作方法是一致的。第二种,发生了隐式的内存泄漏。这时你要重新审查程序的设计,然后仔细研究Performance Monitor记录的Counter的值的变化图,分析其中的变化和程序运行逻辑的关系,找到一些可能的原因。这是一个痛苦的过程,充满了假设、猜想、验证、失败,但这也是一个积累经验的绝好机会。

   总结

   内存泄漏是个大而复杂的问题,即使是Java和.Net这样有Gabarge Collection机制的环境,也存在着泄漏的可能,比如隐式内存泄漏。由于篇幅和能力的限制,本文只能对这个主题做一个粗浅的研究。其他的问题,比如多模块下的泄漏检测,如何在程序运行时对内存使用情况进行分析等等,都是可以深入研究的题目。如果您有什么想法,建议或发现了某些错误,欢迎和我交流。

<------------------------------------------------------------------------------------------------------------------------------>

使用 Performance Monitor 界面使用 Performance Monitor 界面

http://wikis.sun.com/pages/viewpage.action?pageId=78611144

使用 Performance Monitor 界面

本页提供有关 Performance Monitor 图形用户界面的一般信息,包含以下几个部分:

另请参见入门指南中的 [Performance Monitor 1.0 简明教程|http://docs.sun.com/doc/820-7194/gifuc?a=view。

]

菜单

下拉-菜单可让您执行各种操作。大多数菜单项对 VisualVM 来说都很常见。但需要特别注意“工具”菜单,它可用于访问“更新中心”并设置特定于 Enterprise Server- 的选项(请参见下面的说明)。

有关特定菜单项的更多信息,请参见 Performance Monitor 入门指南中的[菜单栏|http://docs.sun.com/doc/820-7194/gifqv?a=view。

]另请参见 VisualVM 文档。

选项

监视选项可以在“工具”菜单中指定。这些选项设置决定树中显示的内容、为图形收集最新数据的频率,以及图形的外观。

选择“工具”>“选项”可以显示常规选项和特定于 Enterprise Server- 的选项。常规选项涉及常规的 JVM 监视设置。有关这些设置的更多信息,请参见 VisualVM 文档。GlassFish 选项涉及特定于 Enterprise Server- 的监视设置(请参见下面的说明)。

Display
决定树中显示哪些内容,隐藏哪些内容。

  • *显示不相关的本地 Java VM。*显示或隐藏非 Enterprise Server JVM。默认情况下,仅显示与 Enterprise Server 有关的 JVM。
  • *显示 GlassFish 系统应用程序。*显示或隐藏 Enterprise Server 上运行的系统应用程序。默认情况下,不显示系统应用程序。

轮询时间间隔
决定通过轮询服务器更新树和图形的频率。减少轮询时间间隔可减少网络和服务器的负载。

  • *实例和群集。*实例和群集的轮询时间间隔。主要用于更新树、群集和节点代理视图。
  • *已部署的模块与资源。*已部署的模块及资源的轮询时间间隔。用于其他表格和顶级模块视图。
  • *HTTP 与线程服务。*HTTP 与线程服务的轮询时间间隔。HTTP/线程时间间隔是最小粒度的时间间隔,用于 HTTP 服务视图、线程视图及其他多种视图。

Graphing
指定与图形有关的元素。

  • *移动平均时长。*多种图形计算移动平均数所使用的时长。
  • *图形线性高速缓存大小。*图形在丢弃数据前收集数据的时长。在停止图形绘制或达到该框中指定的时长前,图形将一直收集数据。达到时间限制后,图形将丢弃旧数据并重新开始收集数据。

Charting
更改图形的绘制方式。

  • *笔画宽度。*图形的线宽。
工具栏

工具栏允许您轻松地执行各种操作。将鼠标悬停在工具栏图标上可以显示更多信息。单击图标即可执行操作。

*“添加 VM 内核转储”和“快照另存为”图标以灰色显示,其涉及的功能在该版本中不可用。
*其他与快照有关的图标涉及 VisualVM 的常规操作。有关更多信息,请参见 VisualVM 文档中的使用快照
*“隐藏非 -GlassFish Java VM”图标涉及本地视图,用于显示或隐藏非 -Enterprise Server JVM。默认情况下,仅显示与 Enterprise Server 有关的 JVM。
*“隐藏系统应用程序”图标可显示或隐藏 Enterprise Server 上运行的系统应用程序。默认情况下,不显示系统应用程序。

该工具栏可以定制,以满足您的需求。有关该工具栏的更多信息,请参见[入门指南]|http://docs.sun.com/doc/820-7194/gifpw?a=browse中的[工具栏|http://docs.sun.com/doc/820-7194/giftm?a=view。

]

Performance Monitor 可以自动检测本地系统上的所有 Enterprise Server 进程。远程进程可使用 JMX 连接进行监视。这些进程显示在左侧“应用程序”窗口的树中,它们按域列出,域下面包含可监视的对象。

*DAS 树视图包含管理服务器实例(服务器)、独立的服务器实例、群集及节点代理的某个节点。
*群集内的服务器实例显示在其对应的群集下面。
*应用程序和其他服务列在相应的服务器实例节点下面。
*展开树中的节点可以查看与这些节点关联的其他服务和应用程序。
*双击-所有节点可以在右侧窗格中打开相关的视图(或者右键-单击并选择“打开”)。但 EAR 是个例外。EAR 没有特殊视图,但可以监视其内容。
*如果无法在树中看到元素,或元素的监视数据不可见,则很有可能是关闭了监视。有关设置监视级别的更多信息,请参见[服务器实例]或 [Web 服务]。
*如果在树中看到有服务器实例,但无法打开,那是因为该实例已停止。
*树开始显示对象时需要花费一些时间。这是由网络拥塞或延迟造成的,请稍候片刻。

有关树的更多信息,请参见入门指南中的[应用程序窗口|http://docs.sun.com/doc/820-7194/gifsl?a=view。

]

图形与表格

*只要打开某个视图,就会开始收集数据。关闭视图即可停止收集数据。该版本不提供 Enterprise Server 数据的持续收集。
*使用图形监视页面时,选中或取消选中复选框即可显示或隐藏视图。隐藏视图后,仍会对其继续监视。
*将鼠标悬停在图形上可以显示描述该图形的工具提示,并提供一份最新数据摘要。该用户界面中的其他元素也有工具提示。
*使用分隔条可以移动图形及用户界面中其他的元素,并调整它们的大小。
*在表格及其他视图中显示数据的初始列表时可能需要花费一些时间。这是由网络拥塞或延迟造成的,可能需要稍候片刻才能显示数据。

选项卡

*打开树中的元素及在 Performance Monitor 界面中查看其他项目时即可显示选项卡。该版本不提供持久性功能。即当您关闭并重新打开 Performance Monitor 时,这些选项卡(以及其他元素的状态)无法保持到新的会话中。
*如果后端服务器断开连接(即 JMX 连接丢失),则选项卡和视图将变为非活动状态并依此显示。该连接的树视图将被移除,但保留根 JMX 节点,此时,其弹出菜单中会出现“重新连接”菜单项。重新连接时必须关闭并重新打开选项卡才能查看新的数据;断开连接前查看的数据将丢失。选择“窗口”>“关闭非活动视图”可以关闭所有非活动视图,以便您能够方便地重新打开这些视图。

另请参见
Performance Monitor 入门指南
Performance Monitor 发行说明
VisualVM 信息和文档


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值