当前位置: 首页 > news >正文

【VS】使用vs2017自带的诊断工具(Diagnostic Tools)诊断程序的内存问题

原文: 【VS】使用vs2017自带的诊断工具(Diagnostic Tools)诊断程序的内存问题

前言

一般来说.NET程序员是不用担心内存分配释放问题的,因为有垃圾收集器(GC)会自动帮你处理。但是GC只能收集那些不再使用的内存(根据对象是否被其它活动的对象所引用)来确定。所以如果代码编写不当的话,仍然会出现内存泄漏的问题,常见的情况有:一个静态变量引用了一个应该被释放的对象,事件注册后不解除注册,非托管资源使用后没有手动释放。不断的内存泄漏终会引起内存不足,挂掉你的程序。

对于这种内存泄漏问题,有很多的分析工具可以使用,常见的有CLRProfiler、ANTS Performance Profiler等。不过从vs2013起,vs自带了一个分析工具-Diagnostic Tool,默认debug时会自动打开,如果没有打开的话就按快捷键Ctrl+Alt+F2。

如下图:
这里写图片描述

开始使用

这里以静态变量持有应释放的对象为例,简单介绍下使用方法。
这里有个winform程序,主要功能即使点击按钮后,输出名为“jim”的个人信息:

private void button1_Click(object sender, EventArgs e)
{
     var p = PersonManager.Get("jim");
     label1.Text += p.Name + " " + p.Age + "\n";
}

Person和PersonManager类的信息如下:

namespace MemLeak
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public byte[] BinaryData { get; set; }
    }
    public static class PersonManager
    {
        static List<Person> people = new List<Person>();
        public static List<Person> People
        {
            get
            {
                return people;
            }
        }
        public static Person Get(string name)
        {
            //正常逻辑
            //var per = People.FirstOrDefault(o => o.Name == name);
            //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;
            p.BinaryData = File.ReadAllBytes(@"D:\2.zip");//一个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<MemLeak.Person>对象的大小是68字节而非独占大小却是195601108字节。
非独占大小的统计方式如下:
这里写图片描述

其中最下层的stringbyte[],由于没有子节点了,所以它们的非独占大小就是它们本身的大小。Address的非独占大小=自己本身的大小+String的非独占大小(50字节)+Byte[]的非独占大小(100字节)=200字节。Customer的非独占大小=自己本身(100字节)+String的非独占大小(100字节)+Address的非独占大小(200字节)=400字节。
所以这也就解释了List<MemLeak.Person>,自身大小仅是68字节,但是由于它是一个List内部包含了5个Person对象,所以它的非独占大小就是5个Person对象大小的和。

3. 哪里产生了泄漏?

这里我先将过滤器里“仅我的代码”和“折叠小型对象类型”前面的勾去掉,便于分析。
这里写图片描述
因为我们值需要看当前程序集(MemLeak)的内存信息,所以在搜索类型名的框里直接输入我们程序集的名称,将其他一些没用的过滤掉。
这里写图片描述
是不是清爽了很多!
分析内存当然要拿占用内存增多最大的对象开刀,点击MemLeak.Person,在下面的“根的路径”视图中可以看到增加的5个Person对象是在一个Person[]数组中。
这里写图片描述
但是我们好像并没有声明过这样的数组,所以继续展开:
这里写图片描述
原来是在一个List里面,而且这个List还是静态的。这时候就要想到内存泄漏的原因,可能就是代码哪里的静态变量持续占有应该释放的对象了。然后更改为正确代码就OK了。

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

切换到的引用选项卡(“引用的类型”)。
这里写图片描述
可以看到本例中PersonByte[]类型的占用内存较多,所以我们就可以重点排查Person中哪里用到了Byte[]类型,进而去优化。

当初取消勾选的那两个选项是什么?

第一个是“仅我的代码”:
勾上之后,会过滤掉那些.Net Runtime产生的一些对象和一些很常见的系统认为与你的程序无关的一些对象。

第二个是:“折叠小的对象类型”:
勾上后,会过隐藏掉那些非独占大小小于托管堆总大小0.5%的对象。将这些小对象的非独占大小合并的父节点中。如下:
这里写图片描述

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

其它

对于生产环境中的软件,分析内存时可以多次抓取dump文件,用vs打开后分析比对。
这里写图片描述
不过只有vs的企业版才有这个功能。


参考

Memory Usage Tool while debugging in Visual Studio 2015

相关文章:

  • 浅谈Golang中select的用法
  • Ubantu 更新时间方法
  • [xdoj] 13011302 数字计数 数字计数的复仇
  • php面试题12(多态web服务器共享session的方法:将session存到数据库)($val=$data[$key];)...
  • nohup和后台运行,进程查看及终止
  • bash命令行初探
  • 转: 关于linux用户时间与系统时间的说明
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • day01.1 vmware虚拟机
  • VMware Vsphere 虚拟化
  • CentOS7切换到root用户和退回普通用户
  • BZOJ5289 洛谷4437:[HNOI/AHOI2018]排列——题解
  • mysql grant授权
  • classloader实战:一个程序使用相同数据库的两个不同版本的jar包
  • 卷积核与特征提取
  • JS 中的深拷贝与浅拷贝
  • 「译」Node.js Streams 基础
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 2017年终总结、随想
  • Create React App 使用
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • Git 使用集
  • HTTP那些事
  • Koa2 之文件上传下载
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • Python中eval与exec的使用及区别
  • Sequelize 中文文档 v4 - Getting started - 入门
  • springMvc学习笔记(2)
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 力扣(LeetCode)965
  • 前端知识点整理(待续)
  • 区块链将重新定义世界
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • $jQuery 重写Alert样式方法
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (1)SpringCloud 整合Python
  • (145)光线追踪距离场柔和阴影
  • (C语言)字符分类函数
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (二)斐波那契Fabonacci函数
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (九)信息融合方式简介
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • ***测试-HTTP方法
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .NET Micro Framework 4.2 beta 源码探析
  • .net 生成二级域名
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .net流程开发平台的一些难点(1)
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • .NET学习全景图
  • .net专家(高海东的专栏)