使用HandleSpy定位托管代码句柄泄漏
来源:互联网 发布:会计电算化是什么软件 编辑:程序博客网 时间:2024/05/12 14:48
引子
我们知道句柄泄漏原因多种多样,一般泄漏的对象主要是内核句柄 、 文件句柄、互操作句柄等。
由于Framework的GC帮我们干了很多事情,所以很多C#程序员养成了吃饭后不洗碗的习惯,new出来的对象基本不考虑如何清理。
一般的对象当然不用考虑,但是遇到需要释放的对象(如IO操作、内核对象创建等),忘了释放,那问题便产生了。
环境说明
本文所有演示均在win7 32位。
需要安装软件:
- VS2010
- dotNetFramework4.0
- WinDbg
- HandleSpy
方案
如果项目业务流程复杂,无法通过简单的二分法定位。那就只有依赖工具了。
之前找了不少的工具来查找,都没有达到预期的目标。
直到那次在知乎上看到了tishion的文章 内核对象&句柄&泄漏&检测 ,才知道HandleSpy这个工具。
这个工具能通过HOOK API的方式判断创建的句柄在一段时间内是否释放,并能显示各种句柄创建的调用堆栈地址信息。
但用这个工具不能直接分析出托管模块的堆栈。与作者沟通后也没有找到合适的解决方案。
想法一:
曾想通过注入进程的DLL调用托管代码提供的API返回堆栈信息,这在理论上当然是可行的。
可实际应用的时候却发现了一大堆问题,比如注入的DLL已经HOOK 了一大堆API,在HOOK的方法里去调用托管代码,又会调用各种已经被hook的方法。
总之,实践说明要将这种方案完美实施可能会耗费大量的时间,最后托管与本地代码之间这样频繁调用会不会出问题还不好说。
所以最后我还是选择放弃了该方案。
想法二:
就在我决定放弃该方案不久,发现WinDbg原来也能调试托管代码。所以决定试试联合Windbg通过堆栈地址定位到具体代码。理论上该方案也可行,而且比前一个方案要优雅那么一点,只不过 需要多动动手。
实施方案
为了实施该方案,新建一个C#控制台应用程序,写一小段模拟句柄泄漏的代码,定时创建一个不会终止的线程。
class Program { static void Main(string[] args) { Thread thd; while (true) { thd = new Thread(new ThreadStart(TestMethod)); thd.Start(); Thread.Sleep(2000); } } static void TestMethod() { while (true) { Thread.Sleep(2000); } } }
然后编译执行程序,我这里叫ConsoleApplication1
打开HandleSpy(注意需要管理员权限),点击选择进程图标。附加到ConsoleApplication1.exe
进程附件后会出现统计句柄窗口,实时显示当前句柄的数量与统计图
等待程序运行一段时间,应该会有大量的句柄泄漏了。点击停止会出现时间线上的句柄个数统计图。
鼠标框选点击右键-》查看选区内函数调用,就可查看泄漏项的堆栈调用信息,如下图所示。CreateEventW是由UnknowModule模块调用托管库完成的。此处有可能是一个泄漏项。 调用的地址就是0x002900e2,仅通过HandleSpy是无法得知该模块的信息的。
接下来重点来了,我要通过WinDBG查找0x002900e2这个地址相关的托管堆栈信息。
打开WinDbg->附加到进程 ConsoleApplication1.exe ,附加之后程序会中断。
输入命令 .load 加载sos扩展
.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll然后!u 查看托管堆栈信息
!u 2900e2
输出如下,可以找到这一行
>>> 0x2900e2 90 nop
往上看即是调用Thread.Start()的方法。由该段输出可知,Program.cs 41行(本地环境该文件中还有不少测试代码)创建的句柄没有在框选范围内释放、 回到源码文件可知,正是 调用 thd.Start(); 这一行
结语
后来发现VS也能加载sos.dll扩展,所以没有winDBG也能完成上述的操作。
在托管代码中,Framework内部的调用或是一些资源回收策略可能导致误判。
也许还有更加简单方便的方法解决此类问题,欢迎各位指点。
阅读全文
0 0
- 使用HandleSpy定位托管代码句柄泄漏
- socket句柄泄漏问题的定位: losf和strace的联合使用!
- 句柄泄露定位——使用windbg(可定位到具体代码)
- 使用GitHub托管代码
- 使用GitHub托管代码
- google 代码托管使用
- 使用GitHub托管代码
- 【代码托管】Github 使用
- 使用GitHub托管代码
- 使用github托管代码
- 使用GitHub托管代码
- 使用Github托管代码
- 发现并防止托管代码中出现内存泄漏,C# 内存泄漏,.net 内存泄漏
- VS检测内存泄漏,定位泄漏代码位置方法
- VS检测内存泄漏,定位泄漏代码位置方法
- 发现并防止托管代码中出现内存泄漏
- 发现并防止托管代码中出现内存泄漏
- 发现并防止托管代码中出现内存泄漏
- 17.struts2_PrepareInterceptor拦截器
- 如何学习
- Selenium2(WebDriver)总结(一)---启动浏览器、设置profile&加载插件
- 递归
- kindel读书笔记——2017.03.22-04.21
- 使用HandleSpy定位托管代码句柄泄漏
- 光猫通过 Telnet 修改自带 WLAN 功能 SSID 强制 ChinaNet- 问题
- Java基础——HashSet源码分析
- Java集合——HashMap原理及要点(一)
- 关于HiveQL的常用语法总结(三)——常用函数
- 由浅入深JavaScript1基础-概述
- 《Ray Tracing in One Weekend》——Chapter 10: Positionable camera
- DF相关问题的处理
- hive原理与源码分析-服务化:LLAP、HiveServer2、MetaStore(七)