.NET Memory Profiler 使用简介
1 简介
.Net Memory Profiler( 以下简称 Profiler) :专门针对于 .NET 程序,功能最全的内存分析工具,最大的特点是具有内存动态分析( Automatic Memory Analysis )功能。
2 安装
安装程序为 SciTech.NET.Memory.Profiler.v4.0.114. 安装+注册机
安装完成后直接覆盖安装目录下的 memprofilerstandalone.dll 、netmemprofilerbase.dll 和 netmemprofilerconsole.exe,然后双击license.reg 文件即可完成注册。
3 使用方法
Profler 可以调试 4 种类型的 .NET 程序 , 分别为 :
l 桌面应用程序
l WPF 程序
l ASP.NET 程序
l .NET Service 程序
对应选择软件的文件菜单如下
Profler 调试共有三种方式选择 :
l 启动跟踪 (Profiler Application)
选定对应的调试方式 , 如调试桌面程序 , 选中 Profiler Application, 然后选择需要启动的执行文件 ,Profiler 将作为宿主程序启动程序开始实时监控内存 .
l 附加进程 (Attach Process)
将 Profiler 附加到指定的进程上 , 此时不能实时监控内存情况 , 只能够收集内存镜像 .
l 导入内存镜像 (Import Memory Dump)
可以选择 dmp 为后缀的内存镜像文件 , 比如 Windbg 以及 DebugDiag 导出的镜像文件 , 此时不能实时监控内存情况 , 只能够收集内存镜像且不能跟踪非托管资源 .
3.1 软件设置
为了加快 Profiler 分析内存类型实例的速度 , 需要设置程序的符号路径即 (Symbol File Locations), 进入菜单 Tool->Options->Preferences->Symobl File Locations, 得到弹出菜单如下图 .
选中 ”Retrive Debug Symbols ..” 选项 , 该选项是为了将被调试程序需要的 PDB 符号文件从 Http://msdl.microsoft.com/download/symbols 下载下来 . 并选定一个目录缓存原来下过的符号路径 , 如果有其他的分目录存放路径 , 则指定 ”Additional Symbols file locations” 选项 .
注 : 如果选择了从微软网站下载符号会影响调试程序的启动时间 , 建议使用本地符号集缓存
3.2 操作说明
3.2.1 启动程序
首先 , 选择需要调试类型 , 调试 ZLBH 桌面程序 , 选择 Profiler Application, 选择好需要启动的程序 exe 文件 .
如果需要设置启动参数 , 则设置好命令行参数以及工作目录 .
选择 ”Next” 进行收集数据的一些选项设置 , 一般直接按 ”Star” 按钮开始调试程序 .
3.2.2 收集数据
选择菜单栏的收集按钮 , 收集堆数据 , 第一个为收集全部堆上的数据 , 第二个为只收集第 0 代的数据 .
3.2.3 重新启动和停止
调试完毕后通过停止按钮跟踪程序 , 通过启动按钮重新启动上一次的调试程序 .
- 启动
- 停止
3.2.4 查看收集数据
Profiler 上有 6 个页卡 , 分别为 :
l Type/Resource 类型 / 资源页卡
l Type/Resource Details 类型 / 资源明细页卡
l Instance Details 实例明细页卡
l Call Stacks/Methods 调用堆栈页卡
l Navtive Memory 本地内存页卡
l Real-Time- 实时跟踪页卡
3.2.4.1 Type/Resource 类型 / 资源页卡
类型 / 资源页卡 , 可以看到当前收集的内存快照的实例数 / 实例字节数等信息 .
通过类型 / 资源网格的上部可以过滤出需要的信息 , 共有四个地方可以过滤 , 从左到右分别为 :
l 资源类型
托管资源
非托管资源
l 警告类型
Profiler 自动分析的内存问题警告类型
l 命名空间
类型的命名空间
l 类型名称
按输入过滤类型名称
类型的过滤还可以通过 ,”Show type/Resource” 下拉框过滤出所有的已有类型 .“Show hierarcical” 通过命名空间分类显示类型和资源 .
Live Instances 列显示当前活动的实例数
Total: 总共建立的实例数
New: 新建的实例数
Remved: 已经销毁的实例数
Delta:New –Removed, 新建和销毁数的差值 .
Comparison SnapShop: 另一个用来比较的内存快照来比较两个快照的差别
3.2.4.2 Type/Resource Details 类型 / 资源明细页卡
通过在 Type/Resource 视图中选中某个类型则显示类型资源的明细信息 , 包括该类型下所有的类型实例 .
左侧包括的信息包括 :
l 是否新建的实例
l 实例号
l 被引用的次数
l 实例所占用的内存大小
l 实例的代信息
l 实例的子级对象所占用的内存大小
右侧包含 Allocation Stacks 和 Shortest Root Paths, 如果不是实时跟踪 , 则没有 Allocation Stacks 页卡 .
l Allocation Stacks 显示的是 Win32 调用路径
l Shortest Root Paths 显示的是从根对象到当前实例的引用路径 , 查看顺序从下往上 , 为根到实例的路径 .
3.2.4.3 Instance Details 实例明细页卡
通过点击 Type/Resource Details 类型 / 资源明细页卡上的单个实例 , 显示这个实例的明细信息 , 显示的主要内容包括 :
l Referenced By 被引用的关系
l References 引用的关系
l Field Value 属性的值
3.2.4.4 Call Stacks/Methods 调用堆栈页卡
显示调用及方法堆栈 , 可以选择只包含托管代码和非托管代码
显示方法所调用的函数及被调用的函数关系,如图:
3.2.4.5 Navtive Memory 本地内存页卡
用于显示进程的本地内存信息,本地内存是被操作系统管理的内存,而不是 CLR 管理的内存。
3.2.4.6 Real-Time- 实时跟踪页卡
如果通过 Profiler Application 调试程序,则能够显示出 Real-Time 页卡,主要内容有:
l Graph and Statistics
通过图形显示实时的内存分配情况,包括:总共实例数、存货实例数、 Disposed 实例数等
l Type/Resources
实时的显示出类型和资源信息,并显示最后一次 gc 存活的实例数以及总共的实例数。
3.2.5 自动内存分析
.NET Memory Profiler 分析工具能够根据内存镜像以及实时跟踪进行自动内存问题分析,提供 6 个严重级别的提示,分别为严重警告、警告、轻度警告、间接警告、建议、提示。
其中对应的严重级别又会有不同的原因分类提示:
严重警告
Potential Memory Leak
潜在的内存泄漏
Disposed instance with direct EventHandler roots
实例已 Disposed 但有直接的 EventHandler 根
一个 Disposed 的实例直接被一个 EventHandler 根化,这个实例只能通过代理访问到
Disposed instance with direct delegate roots
实例已 Disposed 但有直接的 Delegate 根
Undisposed instances (release resource, no finalizer)
没有被 Disposed 的实例
一个 Disposable 实例被 GC 回收,但是因为没有 finalzier 方法而没有正确的 Dispose ,从未导致外部的非托管资源没有被释放掉
警告
Direct EventHandler roots
被一个 EventHandler 直接根化
一个实例直接被一个 EventHandler 根化,需要检查这个实例以及这个 EventHandler 实例,是否实例被 EventHandler 把持
Disposed instance
被 Disposed 的实例
一个实例虽然被 Disposed 但是还是标记为可到达( Reachable ),需要进一步检查该实例是否是活动的( alive )
Undisposed instances (release resource and remove external references)
没有被 Disposed 的实例
一个实例被 GC 回收,但是没有 dispose,Disposable 类型的实例由于没有 Dispose ,导致非托管资源以及外部引用不能被移除
Undisposed instances (release resource)
没有被 Disposed 的实例
Undisposed instances (remove external references)
没有被 Disposed 的实例
轻度警告
Direct delegate roots
直接被代理所根化
一个 Disposed 的实例直接被一个 Delegate 根化,这个实例只能通过代理访问到
Pinned instance
被钉住的实例
钉在内存中的对象因为实例不能移动,会影响 GC 回收效率
间接警告
Disposed instance with indirect EventHandler roots
已 Disposed 的对象被 EventHandler 间接根化
Indirect EventHandler roots
非直接被 EventHandler 所根化
Disposed instance with indirect delegate roots
已 Disposed 的对象被 Deletegate 间接根化
Indirect delegate roots
非直接被代理所根化
建议
Undisposed instances (perform action)
没有被 Disposed 的实例
实例被回收,但是没有正确的 Dispose ,实例在 Dispose 的过程中,需要执行一些 Exit 或 Clearup 操作,包括:写数据到文件、提交或回滚事务、清除缓存、删除临时文件等
Undisposed instances (memory/resource utilization)
实例在 Dispose 的过程中需要 Dispose 其他实例,比如:释放 COM 接口、 suppress finalization
提示
Large instance
大型实例对象
需要存放到大对象堆的实例
Undisposed instances (clear references)
没有被 Disposed 的实例
Dispose 实例需要清空其他实例的引用的操作,但是没有执行
Undisposed instances (no action)
Undisposed instances (unclassified)
4 常见内存问题
4.1 使用了非托管资源的类
非托管资源的类是指本身是被 CLR 管理的,而且其管理的非托管资源也可以被 CLR 自动回收,因为 CLR 只能跟踪非托管资源的生存期,但是不能主动去做 GC ,所以 GC 的时机不确定,所以在使用完后应及时释放。
例如:调用 FileStream
FileStream file = new FileStream(@"c:\Test.txt", FileMode.Open);
连续两次调用程序会报“文件正在使用中”的异常,如果两次调用中间调用强制回收,则不会报异常。
再例如 : 使用 ODP.NET 的 OracleCommand 和 OracleDataReader, 在 Close 后还需要 Dispose;
OracleCommand cmd = new OracleCommand();
cmd.CommandText = sbSQL.ToString();
cmd.Connection = conn;
cmd.Parameters.Add(p1);
OracleDataReader dr = cmd.ExecuteReader();
if (dr.Read())
{
//…
dr.Close();
}
else
{
}
dr.Dispose();
cmd.Dispose();
常见的使用了非托管资源的类如下 :
ApplicationContext
Component
ComponentDesigner
Brush
Container
Context
Cursor
FileStream
DataSet
Font
Icon
Image
Matrix
Texture
OdbcDataReader
OleDBDataReader
Pen
Regex
Socket
StreamWriter
Timer
Transaction
DataReader
Ping
Tooltip
Bitmap
SerialPort
以上列出的类均继承了 IDisposable 接口,需要在使用完后调用 Dispose 方法释放或者使用 Using 语句块,比如 DataTable 、 DataSet 、 DataReader 、 Transaction 、 BitMap…
4.2 Win32API 及 COM
指通过本地 API 函数与托管对象进行交互(比如:通过 P/Invoke 方式调用本地 DLL , DLLImport 声明静态外部函数和 COM Interop )所用到的非托管资源。
例如:当通过 DLL Import 调用 API 函数 GetDC 函数时忘了调用 ReleaseDC 去释放设备句柄造成 4 个字节的内存泄漏。
再如:智能文档中使用的 Word 以及导出 EXCEl 功能用到的 Office 的 COM 非托管组件,在关闭时 GC 不能识别 COM 组件而造成有时候无法对 COM 对象进行释放,这时候可以通过以下两个 InteropServices 函数进行释放
l System.Runtime.InteropServices.Marshal.ReleaseComObject(comObject);
递减与指定的 COM 对象关联的指定 运行时可调用包装 (RCW) 的引用计数。
返回值为关联的 RCW 的引用计数的新值。此值通常为零,因为无论调用包装 COM 对象的托管客户端有多少, RCW 仅保留对该对象的一次引用。
所以通过这个方法显式的通过 CLR 释放非托管 COM 对象上的所有引用。
l System.Runtime.InteropServices.Marshal.FinalReleaseComObject(comObject);
通过将 运行时可调用包装 (RCW) 的引用计数设置为 0 ,释放对它的所有引用。
返回值为与 comObject 参数关联的 RCW 的引用计数的新值,如果释放成功,则为 0 (零)。
4.3 事件造成的内存泄漏
l 当不需要使用事件时 , 应退订事件 , 为了确保安全可以在 Dispose 方法中退订事件 .
l 当对象不再触发事件时,应该将对象设为 null 来移除所有的事件订阅者
4.4 动态添加生成控件造成内存泄漏
动态生成引用了非托管资源的控件后 , 注意一定要 Dispose();
例如 :
RichTextBox rtb = new RichTextBox();
frm.Controls.Add(rtb);
frm.Controls.Remove(rtb);
rtb.Dispose();
下载地址: SciTech.NET.Memory.Profiler.v4.0.114. 安装+注册机
安装完成后直接覆盖安装目录下的 memprofilerstandalone.dll 、netmemprofilerbase.dll 和 netmemprofilerconsole.exe,然后双击license.reg 文件即可完成注册。
分类: asp.net , 开发工具
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于.NET Memory Profiler 使用简介的详细内容...