vs诊断工具vs常见内存泄漏检测工具?详解静态变量、事件与非托管资源泄漏排查

检测维修 0 119

前言

通常来讲,.NET程序员不用为内存分配释放之事忧虑,缘由是有垃圾收集器即GC会自动予以处理。然而,GC仅能收集那些已不再被使用的内存,它是通过依规对 对象是否被其它从事活动的对象所引用来裁定的。所以,要是代码编写存在不妥,依旧会出现内存泄漏情形。例如,一个静态变量引用了本当 release 的object,事件注册之后却未解除注册,非托管资源使用完毕后未手动进行释放这几种常见状况。持续发生内存泄漏将会导致内存在用罄,致使程序崩溃。

有一种内存泄漏问题,存在不少可采用的分析工具,常见的有CLRProfiler和ANTS Performance Profiler等。然而从vs2013开始,vs自身带有一个分析工具,即-Diagnostic Tool,在默认debug时会自行打开,要是没打开的话便按快捷键Ctrl+Alt+F2。

如下图:

开始使用

这里以静态变量持有应释放的对象为例,简单介绍下使用方法。

这儿存在一个winform程序,其最关键紧要的功能在于,当敲按按钮动作之后,便会呈现出称呼为“jim”的个人的相关信息 。

私有,空返回值类型的方法,名叫button1_Click,其有两个参数,一个叫sender,类型是object,另一个叫e,类型是EventArgs 。
{
声明一个变量p,将其赋值为通过PersonManager这个对象调用Get方法,传入参数“jim”所获取到的结果。
将p的名字,加上p的年龄,再加上换行符,添加到label1的文本内容中 。
}

Person和PersonManager类的信息如下:

namespace MemLeak
{
    public class Person
    {
公共的,字符串类型的,名为Name的属性,具备获取和设置的功能 。
        public int Age { get; set; }
公开的字节数组,名为二进制数据,具备获取和设置的属性,且对其拥有者来说,该属性可用于获取或设置字节数组,此字节数组被命名为二进制数据 。
    }
大家共同认可的静态类型的类别之中,存在着一个名为“Person Manager(人员管理器)”的对象集合的管理类别 ,它展现出一种公共的类型。
    {
        static List people = new List();
        public static List People
        {
            get
            {
                return people;
            }
        }
就公众的角度来看,静态层面的个体获取之人,是以字符串形式呈现的名字为依据来进行的,此名字被称作“name”,最终返回的是一个名为“Person”的对象。
        {
            //正常逻辑
声明一个变量per,其值通过People这个集合,使用FirstOrDefault方法,在其中搜索满足条件o.Name等于name的第一个元素,以此来确定per的值。
            //if(per==null)
            //{
            //    per = new Person();
            //    per.Age = 23;
            //    per.Name = name;
于此处,将per.BinaryData赋值为,通过File.ReadAllBytes方法读取位于"D:\2.zip"路径下的文件内容,此文件大小为38MB 。
            //    people.Add(per);
            //}
            //return per;
            //错误逻辑
            Person p = new Person();
            p.Age = 23;
            p.Name = name;

vs 内存泄漏检测工具_CLRProfiler内存分析工具_.NET内存泄漏分析

把位于“D:\2.zip”的文件,以字节数组形式读取全部内容后,赋值给p.BinaryData 。这是一个大小为38MB的文件 。 people.Add(p); return p; } } }

1. 捕获内存快照

程序开始运行之后,从下面这张图能够看到,内存占用处于大约18M的范围,在这一时刻,点击“截取快照”,从而新建一个快照。

然后click按钮5次,然后再点击 截图快照 ,如下:

从曲线图里头能够明显地瞧见内存由18M一步一步地增长至227M 。第二次快照跟第一次相比较而言对象多了19个 ,堆上面的内存使用增多了191M差不多 。

点击图中的(+19)字样,可以进入到快照详细对比界面。

2. 详细对比

如图所示,我们先来看下选中的那一行是什么意思:

MemLeak.Person这个对象,其计数能被看到存在 +5 差异,计数此刻是 5 。这意味着,在两次快照期间,这个Person对象从 0 个变为 5 个,增多了 5 个。大小方面,差异是增加了 195601040 个字节,从字面意思看,这些都比较容易理解。唯一可能存在难度之处,或许是非独占大小(字节),一眼看去,无法直接明白其所起的作用。不过,我们留意到,List对象大小为 68 字节,而其非独占大小却是 195601108 字节。

非独占大小的统计方式如下:

其最下层的string以及byte,鉴于不存在子节点了,因而它们的非独占大小便是它们自身的大小。Address的非独占大小等于自身大小加上String的非独占大小即50字节再加上Byte的非独占_size也就是100字节等于200字节。Customer的非独占大小等于自身100字节加上自己本身(100字节这表述有些重复,可改为自身唯一确定的100字节)再加上之前提到的String的非独占大小100字节加上Address的非独占大小200字节等于400字节。不过这里关于Customer非独占大小计算中“自己本身(100字节)”表述较奇怪,若按前面逻辑统一,可改为“Customer除自身特有的100字节外,其非独占大小还等于String的非独占大小100字节加上Address的非独占大小200字节等于400字节” 。

这就因此作出了说明,List,其自身大小仅仅是68字节,然而鉴于它是一个List,此List内在包含了5个Person对象,所以它的非独占大小便是5个Person对象大小的总和。

3. 哪里产生了泄漏?

我在此先把存在于过滤器里,仅属于我的代码,以及在折叠小型对象类型那儿前面的勾,给去掉,以此来方便进行分析。

鉴于我们仅需查看当前程序集(MemLeak)之内存信息,故而于搜索类型名的框内直接键入我们程序集的名称,把其他一些无用处的予以过滤掉。

是不是清爽了很多!

进行内存分析的时候,自然是选定那个致使占用内存增长幅度最大的对象来着手处理,选择点击 MemLeak.Person,于随后出现在下方的“根的路径”视图里,能够将新增的 5 个 Person 对象清晰看见且它们处于一个 Person 数组中。

但是我们好像并没有声明过这样的数组,所以继续展开:

起初是处于一个 List 当中,并且此 List 还是静态的。在这个时候就需要去思考内存泄漏的缘由,或许就是代码某些地方的静态变量持续占据应当被释放的对象了。随后将其更为正确的代码就没问题了。

4. 查看对象中哪种数据类型占用内存较多?

切换到的引用选项卡(“引用的类型”)。

在本例之中,能够看到Person的Byte类型,其所占内存是比较多的,因而,我们能够重点去排查,Person里究竟是何处用到了Byte类型,然后进一步去做优化 。

其一,当初被取消进行勾选所作的那两个选项究竟是什么呢?其二,“仅我的代码”:一旦勾上它呀呀哪,就会致使去过滤掉那些由.Net Runtime所产生出来的一些对象以及一些极其常见的被系统认定为与你的程序毫无关联的一些对象哪。其三,“折叠小的对象类型”:当勾上这个之后哇哦哦哟,就会使得去隐藏掉那些非独占大小小于托管堆总大小0.5%的对象哟。其四,将这样一些小对象的非独占大小合并到父节点之中呗。情况如下哪:

这两个选项默认都勾上即可。

其它

身处生产环境里的软件,在针对内存展开分析之际,能够进行多次的dump文件抓取操作,当借助vs将其打开以后,做分析比对处理(然而唯有vs企业版才具备此项功能)。

参考:

相关推荐: