Windbg是一款微软开发的调试windows代码的工具,水很深,不过使用windbg来进行clr的调试则比较简单,windbg使用之前需要进行配置。

File->Symbol path-> SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols

windbg的下载地址:http://www.windbg.org/
这里说明一下,下载32位即可,可以运行在64位机器上,如果下载64位可能会导致加载sos的异常进而无法调试。

调试命令说明(SOS.dll):http://msdn.microsoft.com/en-us/library/bb190764

第一步是加载必要的dll用来调试:.loadby sos clr。好的,下面就开始我们的查看内存托管代码的旅程。

首先是eeheap -gc,通过这个命令可以知道现在垃圾回收堆中数据的基本情况。EEHeap在SOS.dll中,定义这个命令是用来显示内部CLR进程消耗内存的情况,gc这个命令是用来表示显示的是垃圾回收里面的情况,如过选择-loader则是显示各个域的数据信息;通过这个命令发现一下到底是gc里面的数据是否异常的大。

如果是,那么接着是dumpheap -stat,这个命令是将gc里面的对象一一开列出来,什么类,占了多少空间。但是别太高兴,因为对于引用对象,对象本身是很小的,关键是引用的对象的判断。所以通过这个我们只能看到是什么占了大空间

通过上面的图片,我们可以发现System.Byte[]占用了较多的空间,我们推测是Tree对象引起的(因为Tree对象使我们自己定义的)

3.!dumpheap -type winformForWinDbg.Tree
通过这个命令看看tree这个对象里面都定义了些什么,都引用了些什么。SOS中定义这个命令的意义是罗列出所有的名字匹配“winformForWinDbg.Tree”的类所在的内存地址

下面有我的代码,大致的逻辑是创建6棵树,这些树没有释放。上下两个列表,上面的列表描述的详细信息,下面的列表描述的一个汇总的信息,可以看到MT值为00406bdc的对象有6个,MT值相同代表着他们的对象类型相同,于是在下面的汇总信息表中我们可以看到6个Segment对象被汇总到了一起。

在实际使用中,如果LOH你判断是来自自己的工程对象,搜索一下工程的命名空间,这样工程内部的对象就会呈现出来。如果是.net的自带对象,这个就有点儿麻烦。

4. !objsize 0224519c
0224519c是明细列表中6个叶子节点中的一个(addresss),依次通过这个命令来获取占用内存数,发现每个都是在100M左右,基本可以判断就是这个Leaf节点没有很好的释放,造成了内存泄露。

问题已经判断出来的,但是如果想要继续使用windebug玩下去,还有的聊。

5.!dumpobj 02213840
通过dumpheap -type我们一共返回了9个对象,6个是leaf对象,那么其他的三个是什么呢?好奇者的天下。

嗯,通过名字我们可以看出来是一个Queue对象,那么这个Queue里面是神马对象呢?注意看下面列表里面有一个m_head类(value值就是内存地址即address),通过访问这个对象也许可以揭开答案。

6.!do 02213858(注:do的意思就是dumpobj)

发现了吗,Queue里面实际是一个Segment类,毫无疑问这个Segment是一个系统生成的中间类,那么探究里面的东西,就是下一步要做的,这回我们看到没有了m_head,取而代之的是m_arry,这说明Segment是一个数组对象(当然更靠谱的方式是看Type列,object[]),那么命令跟着也变了:

7. !dumparray 02213880

理论上面讲,我应该会获得一个31个数组单项,其中前六项是有地址的,地址应该和之前的6个叶子节点的地址一致。可惜我只是得到了31个空的数组,原因不详。但是至此通过Windbg来看C#对象已经很清晰了。以后再面对内存泄露的问题也有应手工具了。

文章借鉴自那些年黑了你的微软BUG

附录:代码段(我将GC回收那一段注释掉了,尽管我本地是Framework4,但是如果手工调用GC仍然没有重现内存泄露的现象,所以索性去掉了,第七步没有得到数组值不知道和这个是否有关系)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices; namespace winformForWinDbg
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} static ConcurrentQueue<Tree> _leakedTrees = new ConcurrentQueue<Tree>(); private static void VerifyLeakedMethod(IEnumerable<string> fruits, int leafCount)
{
foreach (var fruit in fruits)
{
Tree fruitTree = new Tree(fruit);
BuildFruitTree(fruitTree, leafCount);
_leakedTrees.Enqueue(fruitTree);
} Tree ignoredItem = null;
while (_leakedTrees.TryDequeue(out ignoredItem)) { }
} private static void BuildFruitTree(Tree fruitTree, int leafCount)
{
Console.WriteLine("Building {0} ...", fruitTree.Name); for (int i = 0; i < leafCount; i++) // size M
{
Leaf<byte[]> leaf = new Leaf<byte[]>(Guid.NewGuid())
{
Content = CreateContentSizeOfOneMegabyte()
};
fruitTree.Leaves.Add(leaf);
}
} private static byte[] CreateContentSizeOfOneMegabyte()
{
byte[] content = new byte[1024 * 1024]; // 1 M
for (int j = 0; j < content.Length; j++)
{
content[j] = 127;
}
return content;
} private void button1_Click(object sender, EventArgs e)
{
List<string> fruits = new List<string>() // 6 items
{
"Apple", "Orange", "Pear", "Banana", "Peach", "Hawthorn",
}; VerifyLeakedMethod(fruits, 100); // 6 * 100 = 600M //GC.Collect(2);
//GC.WaitForPendingFinalizers();
}
} internal class Tree
{
public Tree(string name)
{
Name = name;
Leaves = new List<Leaf<byte[]>>();
} public string Name { get; private set; }
public List<Leaf<byte[]>> Leaves { get; private set; }
} internal class Leaf<T>
{
public Leaf(Guid id)
{
Id = id;
} public Guid Id { get; private set; }
public T Content { get; set; }
} }

使用Windbg来检查内存的更多相关文章

  1. 使用valgrind检查内存

    Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,是公认的最接近Purify的产品,它包含一个内核——一个软件合成的CPU,和一系列的小工具,每个工具都可以完成一项任务——调试 ...

  2. 用mtrace检查内存泄漏

    http://blog.csdn.net/ixidof/article/details/6638066内存泄漏检查方法(for Linux) 如果你更想读原始文档, 请参考glibc info的&qu ...

  3. 用MFC时,如果程序崩溃,检查内存,然后注意GDI数量,在任务管理器里选项-查看列-GDI数量

    用MFC时,如果程序崩溃,检查内存,然后注意GDI数量,在任务管理器里选项-查看列-GDI数量

  4. 使用PageHeap.EXE或GFlags.EXE检查内存越界错误 (转)

    2011-05-27 20:19 290人阅读 评论(0) 收藏 举报 microsoftdebuggingstructureoutputimagefile 必先利其器之一:使用PageHeap.EX ...

  5. 使用PageHeap.EXE或GFlags.EXE检查内存越界错误

    必先利其器之一:使用PageHeap.EXE或GFlags.EXE检查内存越界错误 Article last modified on 2002-6-3 ------------------------ ...

  6. c++检查内存泄漏

    使用_CrtDumpMemoryLeaks()函数检查内存泄漏 #include <cstdio> #include <cstdlib> #include <crtdbg ...

  7. 如何让xcode自动检查内存泄露

    在project-setting中找到 “Run Static Analyzer” 键,然后把值修改为“YES”.这样在编码的时候,xcode就可以自动为我们检查内存泄露了. 原图片:http://b ...

  8. iOS-如何让xcode自动检查内存泄露

    在project-setting中找到 “Run Static Analyzer” 键,然后把值修改为“YES”.这样在编码的时候,xcode就可以自动为我们检查内存泄露了.

  9. mtrace检查内存泄漏

    内存泄漏检查方法(for Linux) 如果你更想读原始文档, 请参考glibc info的"Allocation Debugging" 一章 (执行info libc);glib ...

随机推荐

  1. (转)tar 解压缩命令

    tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...

  2. 关于编译Lambda时报告返回的为void的错误

    这个错误的信息是这样的: a lambda that has been specified to have a void return type cannot return a value 报告错误的 ...

  3. 使用Inputstream读取文件

    在java中,能够使用InputStream对文件进行读取,就是字节流的输入.当读取文件内容进程序时,须要使用一个byte数组来进行存储,如此会有例如以下两个问题: 1.怎样建立合适大小的byte数组 ...

  4. 标准差(standard deviation)和标准误差(standard error)你能解释清楚吗?

    by:ysuncn(欢迎转载,请注明原创信息) 什么是标准差(standard deviation)呢?依据国际标准化组织(ISO)的定义:标准差σ是方差σ2的正平方根:而方差是随机变量期望的二次偏差 ...

  5. careercup-栈与队列 3.3

    3.3 栈就像叠盘子,当盘子叠得太高时,就会倾斜倒下.因此,在真实的世界中,当一叠盘子 (栈)超过了一定的高度时,我们就会另起一堆,再从头叠起.实现数据结构SetOfStacks 来模拟这种情况.Se ...

  6. 一个基于MVVM的TableView组件化实现方案

    AITableView https://github.com/chentoo/AITableView cocoapods: pod ‘AITableView’ 做什么用? 这是一个简化UITableV ...

  7. GUI编程笔记(java)10:GUI实现一级菜单

    1.首先:菜单组件 MenuBar,Menu,MenuItem              先创建菜单条,再创建菜单,每一个菜单中建立菜单项.              也可以菜单添加到菜单中,作为子菜 ...

  8. scp文件到远端机器问题总结及解决方法

    今天在download服务器日志时遇到了很多问题, 顺便把相应的解决步骤记录下方便以后查看. #把文件copy到192.168.1.102的服务器上 scp -r local_dir readonly ...

  9. php 白屏

    访问php白屏(base on lnmp) vim nginx/conf/fastcgi_param fastcgi_param REDIRECT_STATUS 200; fastcgi_param ...

  10. guid 新建

    var guid = Guid.NewGuid();foreach (var i in new string[] { "P", "N", "B&quo ...