简介和目标
导致.NET 代码性能下降的重要因素之一是内存消耗。 许多开发人员只是主要用执行时间来确定.NET 应用程序的性能瓶颈。 只测量执行时间并不清楚知道性能问题之所在。 好,要说的和要做的一个最大任务就是知道哪些函数、 程序集或类占用了多少内存。 在本教程中,我们将看到我们如何找出哪些函数消耗多少内存。 本文讨论的最佳实践涉及使用 CLR 探查器(CLR profiler)研究内存分配。
请随时到 http://www.questpond.com下载我的涵盖.NET、 ASP.NET、 SQLServer、 WCF、 WPF、WWF的免费500个问题和回答的电子书。
非常感谢Peter Sollich先生
在开始本文时,首先要感谢CLR性能架构师Peter Sollich先生,他给CLR 探查器写了详细的帮助。当你安装CLR 探查器时不要忘记阅读Peter Sollich先生写的详细的帮助文档。
CLR探查器是一个帮助我们对.net代码监测内存分配情况的工具。CLR探查器由微软提供,你可以从下面的网址下载:
http://www.microsoft.com/downloads/details.aspx?familyid=A362781C-3870-43BE-8926-862B40AA0CD0&displaylang=en
注意:CLR探查器有针对.net 1.1和2.0框架的两个版本。2.0框架的版本在http://www.microsoft.com/downloads/details.aspx?familyid=A362781C-3870-43BE-8926-862B40AA0CD0&displaylang=en,1.1的版本在http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en#Overview。
下载并解压CLR探查器之后,可以从bin目录下运行CLRProfiler.exe。
如果你下载的是2.0版本的CLR探查器,它提供针对X86和X64的两种执行环境,清确认你运行了正确的版本。
CLR探查器特性
如果你企图了解.net应用程序代码是如何 进行内存分配的,CLR探查器是最好的工具。它有两个重要的功能:
-
给出一个.net应用程序是如何进行内存分配的报告。这样你就可以看到一个关于每一种数据类型,函数,和方法等是如何进行内存分配的完整报告。
CLR探查器是一嵌入工具,换句话说,它为应用程序内的每一个函数、类、模块在内存使用方面执行它自己的逻辑。比如说你有一个应用程序调用了函数1和函数2,当你使用CLR探查器探测应用程序时,它在每一个函数调用之后插入关于内存堆数据的使用情况,如下
换句话说,不要使用CLR探查器来查看你的应用程序的执行时间,它会减慢你的应用程序10到100倍,你会因错误的结果你死去。
如上所说,它是一个侵入式工具,你永远不要在你的产品环境中使用它。
第一,你永远不要一开始就用CLR探查器来分析你的性能问题。它更多的是用在第二步,在这里你可以调整一个有内存问题的函数或类。更可能的情况是首先使用性能计数器找到哪一个方法或函数的执行用了比较长的时间,然后再使用CLR探查器查看内存分配情况是怎么样的。
怎么运行CLR探查器
当你从微软网站下载CLR探查器后,把文件解压到一个目录中。转到解压后的目录下的Binaries目录,选择并运行‘CLRProfiler.exe’, CLR探查器运行如下图所示。
第一步要确定我们想探查什么,有两个探查选项,一是内存分配,另一是调用方法的次数,然后选择你要探查的数据,点击开始应用程序。
当完成之后,你会看到如下所示的探查结果的完整摘要。那是一个复杂的报告,当我们探查一个简单的应用程序时,我们想看到一个简化的结果。
使用CLR探查器面临的问题
运行CLR探查器时我们面临一些问题。如果我们看到了下面的界面,但它并没有结束,可能有两个原因:
-
你的环境是.net 2.0,但你运行的CLR探查器是1.1的。
-
你没有在GAC中注册ProfilerOBJ.dll。
我们要探查简单的应用程序
我们要探查的应用程序非常的简单,它有一简单的按钮,它调用函数‘UseSimpleStrings’ 和 ‘UseStringBuilders’。这两个函数都连接字符串,一个使用’+’连接,另一个使用‘StringBuilder’类,我们执行连接1000次。
private void UsingSimpleStrings()
{
string strSimpleStrings="";
for (int i = 0; i < 1000; i++)
{
strSimpleStrings = strSimpleStrings + "Test";
}
}
另一个函数使用‘StringBuilder’类进行连接。
private void UsingStringBuilders()
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
strBuilder.Append("Test");
}
}
这两个函数通过按钮的单击事件调用。
private void btnDoProfiling_Click(object sender, EventArgs e)
{
UsingSimpleStrings();
UsingStringBuilders();
}
使用CLR探查器探查我们的简单程序
现在我们了解我们的应用程序,我们尝试使用CLR探查器探查哪一个函数使用了多少内存。然后点击开始应用程序,找到我们的应用程序的exe文件,选上内存分配复选按钮,并关闭应用程序。现在会弹出一个完整的摘要对话框。
点击显示柱状图按钮,你会看到每种数据类型内存分配情况。我知道这非常容易搞混,所以保留了这个截图。
如果你对每一个函数分配了多少内存感兴趣,点击‘Allocation Graph’,它给出每一个函数消耗了多少内存。因为有很多函数,很多方法,所以这个报告常常被搞混,并且我们不能我们的两个函数‘UsingStringBuilders’ 和 ‘UsingSimpleStrings’字符串。
为了简化上面的图形,右击会弹出一些过滤选项,我们使用“查找过程”(‘Find Routine’)搜索过滤掉不需要的数据,我们键入按钮的点击事件名称,从点击事件调用了两个函数。
探索结果放大到如下图所示的方法上,在如下高亮显示的‘btnDoProfiling_Click’框上双击:
双击后我们会看到下面的细节,它现在更好一些。但是第二个函数消失到哪了呢。它只显示出了‘UseSimpleStrings’函数。这是因为这个报告是粗略的,在“所有”(0(everything))上点击你就会看到所有的函数。
现在可以看到另一个函数了。26个字节是什么?那是当函数被调用时额外的字符串操作所必须的,所以我们可以不再会它。我们把注意力集中到现在都可以看到的两个函数‘UseSimpleStrings’ 和 ‘UseStringBuilders’上。你能看到字符串连接消耗了3.8MB,而使用StringBuilder对象只消耗了16KB。所以使用StringBuilder对象比使用简单字符串连接消耗更少的内存。
That was a tough way any easy way(不知道是什么意思)
上面所示的方式感到很吃力,假如你有1000个函数,你想分析哪一个函数消耗了多少内存,事实上是不可能通过查看每一个调用图,来找到你的函数。
最好的方法就是把报告导出到excel中然后再分析,点击‘view’菜单下的‘call tree’。
当你点击‘call tree’之后,就显示如下所示的内容,再点‘view’-‘all function’,就能看到所有的函数,再点文件保存成csv文件。
当导出到csv文件完成后,你就很容易定位到你的方法和函数,并看到分配了多少内存。
使用注释简化结果
如果你知道你要探测哪一个方法,你可以只在当该方法被调用时启用探测器,换句话说,你可以从应用程序启动CLR探测器。
为了从C#代码中启动探测器,首选需要引用‘CLRProfilerControl.dll’,可以从Profiler.Exe所在目录找到这个DLL。
你可以如下代码段所示直接在代码中调用这个探测器控件,我们在调用两个字符处理函数前启用探测器,在调用函数结束后停止探测器。
private void btnDoProfiling_Click(object sender, EventArgs e)
{
CLRProfilerControl.LogWriteLine("Entering loop");
CLRProfilerControl.AllocationLoggingActive = true;
CLRProfilerControl.CallLoggingActive = true;
UsingSimpleStrings();
UsingStringBuilders();
CLRProfilerControl.AllocationLoggingActive = false;
CLRProfilerControl.CallLoggingActive = false;
CLRProfilerControl.LogWriteLine("Exiting loop");
CLRProfilerControl.DumpHeap();
}
现在让我们运行CLR探测器并开始我们的应用程序,请确保你没有启用探测的活动复选项,因为我们会在代码中启用探测器。
现在如果你查看柱形图,你将看到有限的数据,你看到它只记录了‘System.String’ 和 ‘System.Text.StringBuilder’ 数据类型内存分配消耗情况。
如果你查看分配图,你就会看到它现在非常简洁。那个杂乱的视图完全的不见了,现在看到的是一个漂亮简单的视图。在‘Everything’上点击就看到保留的函数。
如前所说,不要完全相信执行时间
在摘要页你能看到 comments 按钮,如果你点击comments按钮就会显示开始时间和结果时间。不要完全相信这个时间记录,我们前面明确的指出过它是一个侵入式工具,所以结果并不正确。
Entering loop (1.987 secs)
Exiting loop (2.022 secs)
结论
-
CLR探测器可以用来查找函数、类和程序集的内存分配情况,并评估性能。
- 它不能用于产品。
-
它不能用于性能评估的开始点。我们应该先运行性能计数器,得到方法的高效执行时间,然后再使用CLR探测器查看真实的原因。
-
可以使用柱形图查看内存分配情况,使用调用图查看方法智能内存分配情况。
- 如果你知道你要探测哪一个方法,你可以从程序启动探测器。
用上面的一切推演出一个作为最佳实践的结论,当你要查看内存分配情况时,没有任何东西能和CLR探测器匹敌。
源代码
可以从这里找到示例中的源代码。
相关推荐
通过实际动手练习,您将学到关于构建Web站点的第一手信息,同时能够深刻理解在浏览器中查看ASP.NET 4页面时,后台到底发生了什么。 这是第七版,也就是本源码书的介绍 The ultimate programming guide to ASP.NET ...
精通ASP.NET 4.0网络编程:基础、框架与项目实战_源码
实践一:Visual Studio 2005中编译调试 实践二:构造个性化网页 实践三:单点登录的实现 实践四:自定义分页显示 实践五:实现数据库应用开发
ASP.NET3.5最佳实践 练习题解答 郑淑芬 http://download.csdn.net/source/2055731 ASP.NET3.5 最佳实践源代码
Spire.BarCode for .Net 是一款专业的免费条形码组件,专为.Net(C#, VB.NET, ASP.NET)开发人员设计,用于生成和读取一维和二维条形码。使用Spire.BarCode,开发编程人员可以迅速轻松地为.Net应用软件(ASP.NET, ...
ASP.NET应用:DataGrid使用最佳实践(ASP.NET应用:DataGrid使用最佳实践代码)
ASP.NET3.5 最佳实践源代码郑淑芬 ASP.NET3.5 最佳实践源代码郑淑芬 ASP.NET3.5最佳实践课后测验解答http://download.csdn.net/source/2055728 ASP.NET3.5最佳实践 练习题解答 ...
《精通.NET互操作:p/invoke, c++ interop和COM interop》一书的源代码
12个asp.net MVC最佳实践 Simone Chiaretta Work for Avanade Italy Microsoft MVP ASP.NET Blogger – http://codeclimber.net.nz Founder of UGIALT.NET OpenSource developer Climber All Around Nice Guy
原装的英文版Asp.net2.0 入门经典C#篇.避免了翻译后的质量下降
此代码是拙文《.net网络编程之一:Socket编程》一文的代码,如果大家对代码有不明白的地方,可以到http://blog.csdn.net/zhoufoxcn/archive/2009/03/18/4000301.aspx查看原文。
ASP.NET 缓存:方法和最佳实践,讲解如何在ASP.NET下使用缓存技术
此为《C#与.NET 4高级程序设计:第5版》中文版一书的源码。 Amazon超级畅销书,权威新版王者归来 全面涵盖C# 2010,用IL深入揭示语言特性 多位微软MVP联手翻译,名著佳译相得益彰 本书是C# 领域久负盛名的经典著作...
ASP.NET应用:DataGrid使用最佳实践
绝大部分异常信息为:System.Net.WebException: The operation has timed out,即网络操作超时的异常
.net 利用WIN32 GlobalMemoryStatus方法以及GetSystemInfo方法 实时监测内存荷载和CUP荷载
.NET程序员进阶修炼的必读之作,由拥有多年开发经验的资深.NET技术专家对C#和.NET中实用的、关键的和难以理解的知识点进行了深入解析,旨在帮助读者在尽可能短的时间内以尽可能低的学习成本去掌握那些最应该被掌握的...
网络是一个流行的高性能JSON为。NET框架 灵活的JSON序列化器对之间的转换。净对象和JSON linq到JSON用于手动阅读和写作JSON 高性能,速度比。净的内置JSON序列化器 写缩进,容易阅读JSON JSON和XML之间进行转换 支持:...
最近纠结致死的一个java报错java.net.SocketException: Connection reset 终于得到解决
ASP.NET第一团购源码(自动部署,有说明) - ASP.NET 团购源码 数据库:Asp.net/MSSQL 第一团购系统是基于Web应用的B/S架构的团购网站,本程序采用目前软件开发IT业界较为流行的ASP.NET和SQLSERVER2000数据库开发...