• 新年+情人节礼物,WinDBG找出你内存溢出的地方


          2010年的silverlight开发中项目组遇到了一些内存过大问题,经过同事们共同努力总算解决了,下面分享我们用WinDBG工具调试的一些经验。下面我们以WinFrom为例(在silverlight,和ASP.NET中基本雷同)。

         首先我们创建一个简单的Winfrom项目,MainFrom为主窗体,Form1和Form2为两个窗体,Form1使用了UserControl1控件,Form2使用了UsrControl2控件。如下图    

         image

          我明年将工程编译好,在bin\Debug目录下启用应用程序,并且启动WinDBG界面如下,将进程Attach进来。

    image

    image

         让您进程继续运行。Windbg附加到进程后会将指定的进程阻塞(类似于IDE中的调试阻塞状态),通过 Go 菜单命令或者输入g命令image 可让进程继续运行。通过Break菜单可让进程阻塞,阻塞后我们就可以通过命令调试。

      加载SOS调试扩展包

          .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll (如果是silverlight则是.loadby sos coreclr,Silvrlight的核心组件为coreclr).

          image

        下面我们将Form1和Form2窗体都打开两个个然后关闭,接着手动调用一次GC回收(因为本次我们主要看GC不能回收的对象)。

        查看内存中所有对象

              !dumpheap –stat

              !dumpheap的参数规格为

           !dumpHeap [-stat]
              [-strings]
              [-short]
              [-min <size>]
              [-max <size>]
              [-thinlock]
              [-startAtLowerBound]
              [-mt <MethodTable address>]
              [-type <partial type name>]
              [start [end]]

    image

    通过 type 参数查看内存中指定类型的对象 !dumpheap –type WinDBG,支持模糊查询,下面我们就是查询全名包含WinDBG下的所有未回收的对象。

    image

    我们可以看到名称包含WinDBG的有一个MainFrom未回收,两个UserControl1未回收。

    分析:MainFrom未关闭,没有回收是正常现象,UserControl1两个未回收就不正常了。

    下面我们需要找出UserControl1为什么没有回收掉了。我们知道对象如果不能被GC回收的情况有:1.引用了Com组件;2.有依赖某个其它的对象,这个对象不能回收;3.等。现在我们就猜测为以上两种情况。

    开始查找原因:

        1.首先我们找到UserControl1的内存地址,用-mt参数dump出内存地址 !dumpheap -mt 002b7efc。002b7efc为UserControl1的类型地址。

    image 2.我们可以看到两个UserControl1的地址分别为01bc9018 和01bcde5c .

       我们用gcroot命令查看对象的引用关系,gcroot的完整参数是这样的!GCRoot [-nostacks] <Object address>

    image image 为重点怀疑对象,下面排查代码。

    public UserControl1()
    {
        InitializeComponent();
        Application.ApplicationExit += new EventHandler(OnApplicationExit);
    }

    private  void OnApplicationExit(object sender, EventArgs e)
    {
    }

    Application事件为应用级别,我们可以确定是因为它引起对象不能回收,因为Application的只有在程序退出时候才会销毁。

    附:

    命令 描述

    BPMD [<module name> <method name>] [-md <MethodDesc>]

    建立一个断点在指定模块的指定方法上。

    如果指定模块和方法尚未被载入,该命令等到该模块被载入并且被即时(just-in-time)编译的通知后再建立断点。

    CLRStack [-a] [-l] [-p]

    只提供托管代码的栈跟踪。

    -p 选项显示托管函数的参数。

    -l 选项显示在一个框架里局部变量的信息。SOS调试扩展无法检索局部变量的名字,所以局部变量的输出格式为<local address> = <value>。

    -a (all) 选项是-l-p组合的快捷方式。

    在x64和基于IA-64的平台上,SOS调试扩展不显示过渡框架(Transition Frames)。

    COMState

    列出每个线程COM单元模型和可用的上下文指针。

    DumpArray [-start <startIndex>] [-length <length>] [-details] [-nofields] <array object address>

    -或者-

    DA [-start <startIndex>] [-length <length>] [-detail] [-nofields] <array object address>

    检查一个数组对象的元素。

    -start 选项指定显示元素的起始索引号。

    -length 选项指定要显示的元素数目。

    -detail 选项按照DumpObjDumpVC格式显示元素的细节。

    -nofields 选项使数组显示不包括字段。仅当指定 -detail 选项时该选项才可用。

    DumpAssembly <Assembly address>

    显示一个汇编集的有关信息。

    如果存在多个模块,DumpAssembly命令将它们全部列出。

    你可以用DumpDomain命令得到汇编集地址。

    lm

    显示已加载模块

    DumpDomain [<Domain address>]

    枚举在指定AppDomain对象地址里面装载的每一个Assembly对象。当不带参数调用DumpDomain命令时,它列出一个进程中所有的AppDomain对象。

    DumpHeap [-stat] [-min <size>][-max <size>] [-thinlock] [-mt <MethodTable address>] [-type <partial type name>][start [end]]

    显示关于垃圾收集堆的信息和有关对象的收集统计。

    DumpHeap命令如果在垃圾收集器堆中检测到过多的碎片,它显示一个警告。

    -stat 选项限制输出内容只有统计的类型摘要。

    -min 选项忽略那些尺寸小于size参数的对象,以字节为单位。

    -max 选项忽略那些尺寸大于size参数的对象,以字节为单位。

    -thinlock 选项报告ThinLocks。更多信息请看SyncBlk命令。

    -mt 选项只列出符合所指定MethodTable结构的那些对象。

    -type 选项只列出类型名字子串匹配指定字符串的那些对象。

    参数 start 指定开始列出的地址。

    参数 end 指定停止列出的地址 。

    U 反汇编
    所有扩展命令的起始符
    . 所有元命令的起始符号

    更多命令可参考张银奎的《软件调试》,新年刚过有些潦草,望园友见谅。

    代码下载点击此处

  • 相关阅读:
    第二次作业
    复盘一个商品期货的通用模型
    C#如何获取枚举(Enum)变量的值
    [C#]Socket通信BeginReceive异步接收数据何时回调Callback
    [C#]浮点数除零无法捕获异常的解决办法
    js对字符串进行编码方法总结
    web最全资源网址
    简单粗暴地理解js原型链--js面向对象编程
    常见前端九十道面试题及答案-韩烨
    C语言文件读写,复制
  • 原文地址:https://www.cnblogs.com/ForrestZhang/p/WinDBG.html
Copyright © 2020-2023  润新知