(转).NET Memory Profiler 使用简介

1         简介

.Net Memory Profiler(以下简称Profiler):专门针对于.NET程序,功能最全的内存分析工具,最大的特点是具有内存动态分析(Automatic Memory Analysis)功能。

2         安装

安装程序为SciTech.NET.Memory.Profiler.v4.0.114. 安装+注册机

下载地址:http://download.csdn.net/detail/wmlunge/4972685

安装完成后直接覆盖安装目录下的 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    Win32APICOM

指通过本地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 文件即可完成注册。

posted @ 2016-01-14 20:32  海上浪子  阅读(8047)  评论(1编辑  收藏  举报