哈哈,昨天没事做,在CodeProject瞎逛,偶然看到这篇文章,居然读得懂,于是就翻译了一下,当练习英语,同时增强对文章的理解,发现再次翻译对于文章的一些细节问题又有更好的理解。下面是翻译内容,虽然翻译出来后很像小学生写作文,读起来很拗口,希望大家多多提出宝贵意见,谢谢。
原文地址:
检查.net代码中占用高内存函数
介绍
非常感谢 Mr. Peter Sollich
使用 CLR profiler
CLR profiler 功能
不要在产品中使用CLR profiler或者一开始就作为性能检测工具
如何使用CLR profiler?
使用CLR profiler可能遇到的问题?
一个简单的示例
使用CLR profiler来检测我们的示例
其它更简便的方法
使用comments简化结果
不要太过于关心执行时间
总结
简介
.net中内存消耗是一个减低性能的重要因素。许多开发者都只关注执行时间使.net应用程序出现性能瓶颈。
但是仅通过执行时间来并不能很清晰的判断性能问题所在。好吧,那么最重要的问题就是去理解,哪个函数,
程序集或者类消耗了多少内存。在本篇介绍中,我们将会知道如何找出那个函数占用了多少内存。本问主要讨论
使用CLR profiler来了解内存分配的最佳实践。
非常感谢 Mr. Peter Sollich
在开始本篇文章前,我们首先感谢CLR 性能架构师Peter Sollish,写了如此详细的帮助文档。安装CLR profiler后
请别忘了阅读Peter Sollich写的详细帮助文档。
非常感谢,如果你看到我这篇文章,请让我知道。
使用 CLR profiler
CLR profiler 工具是由微软提供的,用于检测你的.net代码中是如何分配内存的工具。你可以通过下面链接下载:
http://www.microsoft.com/downloads/details.aspx?familyid=A362781C-3870-43BE-8926-862B40AA0CD0&displaylang=en
注:CLR profiler有两个版本,一个是.net 1.1,另一个是.net 2.0. 对于2.0 CLR profiler,你可以通过
http://www.microsoft.com/downloads/details.aspx?familyid=A362781C-3870-43BE-8926-862B40AA0CD0&displaylang=en 下载
或者通过http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en#Overview 下载1.1版本
下载CLR profiler,解压缩后,就可以在Binaries文件夹下运行程序。
如果你下载了2.0的CLR profiler,它提供了x86和x64两种环境,所以请确定运行正确的版本。
CLR profiler 功能
CLR profiler是用于理解.net应用程序中内存如何分配的最好工具。它的两个主要功能:
提供.net 应用程序内存分配的完整报告。所以你可以查看每一个类型,函数,程序集的内存分配报告
提供了调用方法所消耗的时间
不要在产品中使用CLR profiler或者一开始就作为性能检测工具
CLR是一个注入式工具。换句话说,它使用自己的逻辑来将转储应用程序中每个函数/类/模块的内存。
也就是它会干扰原有程序逻辑。例如,一个应用程序调用function 1和function 2,当检测你的程序时,
CLR profiler会在每个函数执行后注入内存堆数据,如下图:
换言之,你不能使用CLR profiler来查看程序的运行时间,因为事实上它会使你的用于程序慢到10到100倍。
你会得到一个错误的结果。
因为它是这样的一个注入式工具,所以不要在生产环境中使用。
首先,不要一开始就使用CLR profiler工具来分析你的性能问题。它只是在当你发现某个函数或者类有内存问题时才使用。
最合适的方法是使用性能计数器查找那个方法或者函数占用了较长时间,然后再使用CLR profiler来查看内存占用情况。
如何使用CLR profiler?
从微软下载CLR profiler,解压文件。打开解压文件夹中的Binaries目录,找到合适的版本,运行‘CLRProfiler.exe’。你会看到
如下图界面。
首先选择我们需要检测的程序。有两样东西可以检测的:一个就是内存的分配另一个则是一个方法被调用的次数。
勾选你需要检测的内容,点击“start application”。
检测完成后,你可以看到像下面图片中的一个概要。它是一个非常复杂的报告,之后我们会通过一个简单的例子来看简单流程。
使用CLR profiler可能遇到的问题?
当运行CLR profiler时,我们可能遇到一些问题。如果你看到以下界面同时程序不会停止时,可能是以下两个原因造成的:
你使用了.net 2.0但是运行的是CLR profiler 1.1
你没有在GAC注册RrofilerOBJ.dll
一个简单的检测例子
我们要检测的例子非常简单,使用一个简单的按钮,调用两个函数"UseSimpleStrings"和"UseStringBuilders".
两个函数都是用来连接字符串。一个使用"+"号连接,另一个则使用StringBuilder类。我们循环连接1000次。
{
string strSimpleStrings="";
for (int i = 0; i < 1000; i++)
{
strSimpleStrings = strSimpleStrings + "Test";
}
使用StringBuilder类连接
{
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
strBuilder.Append("Test");
}
}
两个函数都通过按钮时间调用:
{
UsingSimpleStrings();
UsingStringBuilders();
}
使用CLR profiler来检测我们的示例
接下来我们将使用profiler来检测我们例子中的函数占用了多少内存。点击“Start Application”,
选择应用程序,然后点击应用程序中的按钮,然后关闭应用程序,你会看到弹出一个概要窗口。
点击柱状图,你可以看到每种类型的内存分配情况。我知道这非常混乱。所以我们不管它。o(∩_∩)o
如果你对没有函数是分配了多少内存,你可以点击"Allocation Graph"。它会告诉你每个函数分配了多少内存。
不过这个报告非常复杂,因为里面有好多函数,我们很难去定位到我们的两个函数"UsingStringBuilders"和"UsingSimpleStrings"
为了简化上面的图片,我们使用右键中的一些过滤。我们使用"Find Route"过滤一些不必要的数据。输入按钮的事件名
从这个事件,我们就可以找到调用的两个函数。
搜索后图片移动到如下图所示。双击下图中高亮的"btnDOProfiling_Click"框。
双击后,你可以看到如下图的详细信息。现在好点了,但是第二个函数不见了,只显示了"UseSimpleStrings"函数。
这是因为这个报告是大概的,所以选择“Detail”区域中0(everything)即可以看到所有函数。
现在你可以看到其它函数了。那26bytes是啥呢?它只是函数执行时,对字符串的额外操作。所以我们可以忽略它
让我们来关注一下"UseSimpleStrings"和"UseStringBuilders"。你可以看到简单字符串使用3.8M。而StringBuilder只使用了26kb
因此,StringBuilder比简单的字符串连接要省更多的内存。
That was a tough way any easy way(这句不知咋翻译)
上面使用的方法比较麻烦。假如你有1000个函数,你要检测这些函数占用的内存。这是非常不可能去看每个调用图,找出你的函数。
最好的方式就是导出一个详细的报告到excel文件,然后分析这些数据。因此,点击"view"中的"call tree"
点击call tree后弹出如下对话框。点击view 中的"all function"。你尅看到所有的function,点击File中的Save保存为CSV文件。
导出成功后,你就可以很方便的定位到你的方法或者函数,查看分配了多少内存。
使用comments简化结果
如果你知道你要检测哪个方法,你可以在方法调用的时候启动profiler。也就是说你可以在应用程序中启动CLR profiler.
为了在C#代码中启用profiler,你首先必须添加"CLRProfilerControl.dll"的引用。你可以在profiler程序所在文件夹下找到该dll
这样你就可以直接在你的代码中调用profiler了,如下代码段。我们在调用两个字符串连接函数之前先启用profiler。
函数调用完成后,再禁用profiler
{
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 profiler启动程序,因为我们在已经在代码中启用profiling,所以我们就勾除Profiling active选项。
这时候你可以看到柱状图数据比较少,你会看到他只记录了"System.String"和"System.Text.StringBuilder"两种类型的内存分配。
但你看到分配图时你会发现变得非常简洁。之前凌乱的视图不见了,取而代之的是简单集中的图片。记得点击"Everything"查看所有函数。
不要太过于关心执行时间
在Summary界面,你可以看到一个"comments"按钮。点击该按钮,你可以看到程序的开始和结束时间。请不要在意这个时间记录,就像
我们之前提到的,一个侵入式工具返回执行时间结果是不正确的。
Entering loop (1.987 secs)
Exiting loop (2.022 secs)
总结
CLR profiler可用于查看函数,类和程序集的内存分配,以评价程序的性能。
它不应该在生产环境中使用。
它不应该是性能检测的首选工具,我们应该首先选择性能计数器,获取那个方法执行时间较长,然后在用CLR profiler找出真正原因。
你可以使用柱状图来查看内存分配,函数调用图来查看函数所占用内存。
如果你知道哪个函数要检测,那么你可以在程序中启动profiler
总而言之,作为一个查看内存分配的工具,没有任何其它工具能够比得上CLR profiler了。