在的 MSDN 杂志六月 2009年文章的.NET 应用程序的审核内存使用量",我们讨论了使用任务管理器,PerfMon VADump 之类的工具的监视内存消耗。这些工具帮助监视您的应用程序的总体内存消耗量。通常,如果.NET 应用程序正在使用大量的内存,则或者因为该应用程序加载大量的 DLL 或应用程序在.NET 垃圾回收 (GC) 堆上创建大量的长寿命的对象。如果应用程序加载多个 DLL,将仅 recourse (除以避免不必要的依赖项) 是在运行代码更少。但是,如果从一个大 GC 堆内存占用量受到影响的应用程序,一个可以标识在 GC 堆已显著造成内存消耗,如六月文章中所述。在本文中,我们完成 GC 堆无关的内存问题的图片通过使用 CLR 分析器.NET GC 堆内存调查提供分步指导。
概述:审核所有应用程序
.NET 运行时抽象某些操作 (如分配内存或使用较大的类库) 并且不是始终简单预见这些操作对应用程序性能的影响。尤其是对于如此内存。重要 takeaway 本文最后一个点是每个应用程序的工具应得一个定期内存审核。鏈 经历此分步实际程序用作我们的示例 (相对于演示)。这一简单的程序,我们可能没有选择审核该应用程序已经内存使用情况。但是,我们完成了内存审核小工作量执行审核的支付关闭 handsomely,我们找到了大量内存不足。
我们在本文中使用该程序称为 XMLView,简单的 XML 文件的查看器是高度可伸缩的。XMLView 可以轻松地处理千兆字节大小的 XML 文件 (尝试在 IE 中 !) 由于此应用程序被设计为开始从有效的内存。尽管一些的方案中的该仔细设计工具 (如任务管理器显示得更多的内存比我们将具有预期的应用程序使用。本文将引导您发现性能 Bug 的调查。
大图片内存使用情况
一个内存审核的第一步是启动应用程序并查看操作系统任务管理器来检查应用程序已经总体内存使用情况。我们上述跃点数最重要的是"内存的专用 Working Set"列。这是指不能与其他计算机上运行的进程共享的应用程序内存 (请参阅上一项目详细信息)。
打开任务管理器后您可以监视实时应用程序的专用内存使用情况,通过各种用户方案运行并观察内存消耗将任何影响。有两个异常,保证您的注意。内存消耗 disproportionate 方案或正在执行任务的一个。渚嬪的方式 打开一个菜单项,或执行简单的操作不应导致大峰值内存消耗中。内存泄漏是异常的需要注意的第二个类型。某些操作 (,例如打开一个新的文件,预期为将创建新的数据结构每次执行该操作。但是,许多操作 (渚嬪的方式 搜索) 不从逻辑上分配新长期存在的内存。不分配内存的第一次 (如填充惰性数据结构),它们执行这些无状态的操作不正常。但濡傛灉鍐呭瓨浣跨敤缁 х 画增加与后续迭代的方案,正在泄漏内存,并需要进行调查。使用任务管理器的这种技术是非常粗糙的设计 (只在大内存问题将明显),但对于许多应用程序,只在大的价值追求。
为 XMLView,方案启动一个大的 XML 文件查看器,执行各种导航操作打开树节点或搜索等。此实验最初打开 (内存使用情况稳定后一段中 ; 不泄漏参见图 1)。内存使用情况而显著 spiked (8 MB) 时,搜索功能从 16812KB 私人工作集用于 25648KB (请参阅的图 2)。这表示我们正在寻找该异常情况之一:资源消耗了无法容纳我们精神模型,该程序已经内存的行为,因为搜索没有辅助数据结构,因此不应使用额外的内存。我们必须解决的一个神秘。
图 1 XMLView 内存使用在任务管理器中搜索操作前
图 2 XMLView 内存使用在任务管理器在搜索操作
CLR Profiler
许多第三方工具是用于进行分析的.NET GC 堆 ;但是,我们将着重具有可免费从 Microsoft Web 下载的优势的 CLR 分析器。在"CLR 分析器"网页搜索将定位在下载网站并安装组成只解压缩的文件 (没有没有正式的安装步骤)。作为包的一部分,CLR Profiler.doc 该文件提供了该文档。在这篇文章,我们将重点介绍通常使用的部分。
CLR 分析器是如何工作的?
为了正确使用探查器,您需要了解如何收集数据和工具的限制。CLR 分析器使用特殊接口,运行库允许它运行库 (msdn.microsoft.com/library/ms404511.aspx) 中的特定事件发生时,获取回调接口。当前,与通知有关该联系人的机制来探查器运行库的特定环境变量集启动正在分析过程时才可以使用此接口。因此,有如无法附加到现有进程的一些限制。CLR 分析器必须开始设置了正确的环境变量。服务通常由启动的操作系统直接,类似于 ASP.NET,特殊过程必须遵循 (从下载的详细信息,请参阅 CLR Profiler.doc)。将一个顺便,对于某些方案,我们已添加的 API 支持附加.NET 框架 4 (但仅 API 支持 ;支持附加的 CLR 分析器工具将按照后不久)。
CLR 分析器启动应用程序时, 它提供了它所收集的信息的类型上的选项。默认情况下, 它获取回调每次托管的方法调用或返回从一个调用,以及每个 GC 堆分配。这些回调称为通常导致程序显著降低,并创建较大的日志文件的多个 100 MB) 为许多现实方案。CLR 分析器不测量执行时间,并跟踪仅 GC 堆分配的每个不同的方法调用的次数来限制其自身。CLR 分析器使用来跟踪哪些方法个堆栈上, 当前活动,所以每次调用和堆分配可能由于与事件的发生位置的完整的调用堆栈的 方法调用的方法返回事件。这标识在创建对象。
遗憾的是,CLR 分析器在默认情况下收集的详细的信息可以使数据集合过程非常慢 (分钟)。但是,很少需要此详细的信息。一种最有用的技术不需要任何附加信息才能正常执行过程中收集。尽管了解内存分配发生很多错误可以修复仅了解保留的内存处于活动状态。若要回答这个后者的问题,一个不需要调用堆栈或分配事件 ;而是,只有在 GC 堆的简单快照需要。因此的大多数调查,一个可以关闭所有普通的事件日志记录功能,从而获得好的体验 (合理分析性能,以及标识该问题)。这是在此处使用该技术。
使用 CLR 分析器
如果从网站下载 CLR 分析器其提取到默认位置程序最终会在 C:/CLR Profiler/Binaries/x86/ClrProfiler.exe (没有 X64 版本,过)。启动 CLR 分析器调出窗体中的 图 3 所示。
图 3 CLR 分析器启动屏幕
此启动对话框控件的配置文件数据的集合。复选框指示在执行应用程序时分析将处于活动状态和分配和调用的数据将被收集 (该日志文件将详细)。由于我们感兴趣只堆快照仅分析活动必须继续保持选中状态 (分配和电话框应未选中)。因此,CLR 分析器将执行程序,保持响应和向下的日志文件大小的过程中记录仅简略最小值。
如果您想要配置文件应用程序不需要任何命令行参数或特殊的启动目录,可只是启动应用程序和选择 CLR 分析器将调出一个文件选择器对话框,选择运行该应用程序。如果您需要设置命令行参数或应用程序的当前目录,则应设置它们使用文件->单击上启动应用程序之前设置参数的菜单。
获取堆快照
选择应用程序可执行文件后, CLR 分析器将立即启动运行应用程序。因为我们关闭了分配和调用分析,它应运行非常接近其正常速度。CLR 分析器现在附加到该的应用程序,并且在任何时候,可以选择立即显示堆,然后获取堆快照。堆快照 XMLView 应用程序执行的操作类似于图 4 中显示的内容。
这是在整个 GC 堆的表示形式 (仅活动对象所示)。而在 GC 堆是实际的任意图,CLR 分析器确定链接和窗体出它在一个 pseudo-node 称为根的根节点的树。属性的每个子级大小可以然后被化到其父以便根目录,所有活动的对象已被考虑。在上述示例中在 GC 堆必须 5.4 MB 的对象。此 5.4 MB 的未分配,GC 堆中不包括的可用空间,而因此,为堆分配的虚拟内存量将保证更大 (准确的大小取决于,但以前的文章所述大 1.6 倍是一个很好的估计值)。
通常,没有多个单个路径从根到一个特定的对象。CLR 分析器始终在树中,选择显示的最短路径。但是,如果多个具有相同的长度的路径选择由任意。此外,默认,CLR 分析器不显示单个对象,但处理所有具有相同的父类型和与同一个节点的一部分相同的子类型的对象。此方法通常会折叠链接的列表节点,从而简化了视图其他递归数据结构的一个好。如果不需要此聚合,则您可以关闭它通过右击并选择显示单个实例。
在堆图表窗口的顶部是两个单选按钮组。第一个是该范围表示在图表视图中的每个框的高度。第二个是确定大小的一个节点必须是获取显示详细信息按钮。非常有用将设置此内容将关系图混乱非常粗糙。通常,您感兴趣只具有某些与特定类型 (例如仅有关 XmlPositions 类型的对象) 的图形。CLR 分析器允许您这和其他条件进行筛选 (右键单击->筛选器)。
在 图 4 中 XMLView 示例中,在 GC 堆 (5.1 MB) 的大部分是实例的 XmlView 类型的子级。XmlView 实例具有 XmlPositions,又具有一个 5 MB XmlElementPositionInfo 数组,数组类型的子级。XmlElementPositionInfo 是较大的跟踪文件中的每个的 XML 标记起始点的数组,其大小是读取 XML 文件的大小成比例 (因为这是一个非常大的文件,预期该数组将大)。因此,我们结束是合理的堆大小。
图 4 CLR 分析器快照前搜索操作
跟踪 GC 堆增长
XMLView 的示例中我们的问题未用的 GC 堆,但这一事实已完成搜索后会显著增长,初始大小。因此,什么我们感兴趣是两个快照之间堆中的更改。CLR 分析器具有特殊的功能,只是为了执行这种调查。
后采取一堆快照,我们可以然后执行搜索操作 (导致分配额外的内存),然后执行另一堆快照。结果是下面的显示:
现在与 XmlView 对象相关联的内存已扩展为 8.5 MB (从 5.1 MB)。节点有两个的颜色变浅红色表示不存在前一个快照中的内存。暗红色的唯一新快照的内存。因此,通过调用 (这不是令人惊讶) XmlFind,类型添加新的内存在 图 5 中可看出。通过滚动到右侧,我们发现此额外的内存的是相关联的 Int64 数组属于类型 LineStream,作为显示在的图 6。
这是标识该 Bug 所需信息!事实证明.NET XML 分析器能够获取行号列的数量的 XML 标记,但不是其文件位置。要解决此问题,程序所需行号映射到文件的位置。为此使用 Int64 数组。但是,代码需要此映射只在 当前 XML 元素标记,可能跨越行,但通常只需几次任意数量的。通过允许增加不必要地大映射数组的查找代码,不是重新此映射的设置。淇 绋嬪簭简单,但如果不能执行内存审核此错误将已存在可能永远。使用 CLR 分析器,花费仅几分钟时间来查找关键修复此错误的信息。
图 5 CLR 分析器快照后搜索操作的部件 1
图 6 CLR 分析器快照后搜索操作的部件 2
显示新的单独对象
在前面的示例,它是相对容易地以可视方式区分其颜色由唯一上次快照对象。然而的大型对象图表,这是难以执行,并且会非常方便,完全,从关系图中移除旧对象可以完成的使用 CLR 分析器。此功能将分配事件用于实现,因此后采用第一个快照,您需要检查分配 (可以将呼叫框未选中)。与应用程序交互,直到它分配更多的内存并选择显示堆立即再次下这。它将显示为之前堆包含其两种颜色的红色。现在,可以右键单击窗口中选择显示新对象并将出现一个新的窗口,显示仅在 GC 堆分配两个快照之间的部分。
跟踪所有根对象
您可能想知道如何最终给垃圾回收器的托管代码可以发生泄漏的内存清理所有未使用的内存。但是,情况的项目可以保持处于活动状态多长时间后您可能认为它们将被释放。渚嬪的方式 非有意的引用可能会使托管的对象处于活动状态。
跟踪内存泄漏,技术是非常类似于用于了解 disproportionate 内存消耗。唯一的真正区别在于用于消耗的内存 disproportionate 执行期望为 (它只是不应为大) 的内存而的泄漏,我们不希望该对象在 GC 堆中所有对象。使用堆快照和显示新对象功能,很容易识别的对象不应该处于活动状态 (使用您对该方案的理解)。如果对象在堆中存在,必须有对对象的某些引用。但是,如果有多个单个引用,您可以删除一个链接不能解决该问题由于另一个引用保持活动状态。因此,会方便地查找保留特定的对象处于活动状态在 GC 堆中的所有路径从根。CLR 分析器具有执行此操作的特殊支持。第一次,感兴趣的节点被选中。然后用鼠标右键单击并选择显示参考,和新图形将其显示显示从根对象的所有路径有问题。这将为您提供足够的信息标识的引用: 保持对象堆中处于活动状态。消除链接到该对象可以解决您的内存泄漏。
获取调用堆栈分配
查看在 GC 堆足以经常诊断最高的内存消耗问题。然而,在某些情况的下很有用了解调用堆栈分配发生的位置。若要获取该信息,必须选中之前执行程序的分配复选框。这将导致 CLR 分析器分配发生时登录堆栈跟踪。收集此信息将放大该日志文件,但它将仍通常是小于 50 MB 的中等复杂的应用程序的。因为这会导致每个方法为项是详细日志记录应不打开电话复选框 (它是易于获取日志文件大于 100 MB)。只有您使用 CLR 分析器来确定哪些方法被称为通常,需要此方法的调用信息,这不在一个调查,内存中通常很有用。
在堆关系图中选择感兴趣的节点,用鼠标右键单击,然后选择显示用户分配。显示所有的调用堆栈分配的对象,该节点中的一个新窗口将显示。您也可以从视图中的访问,堆栈跟踪 >分配图表菜单项。这显示了到调用堆栈分配它们的属性化程序期间所做的所有分配。对于内存调查,该视图不非常有用因为这些分配的很多的快速获得由 GC 回收,并因此,并不参与总内存消耗的临时对象。但是,由于 CPU 使用率 (分配,然后回收) 并参与这些分配,该视图时您的应用程序是 CPU 并且 CPU 探查器显示大量的 GC 所花费的时间。将剪裁停机时间的 GC 明显的方法是执行更少的分配,此视图显示在您分配的调用堆栈。
在开发生命周期的一部分
在本文中,我们遍历通过一个内存性能调查,对于其中内存消耗通过.NET GC 堆 dominated 常见的情况。在使用该免费和可下载 CLR 事件 € 探查器我们能够轻松地获得应用程序中的各个点在 GC 堆的快照,并比较与另一个快照。使用 CLR 分析器,它易于执行的审核 (它只需要几分钟) 并且应该是开发生命周期的每个应用程序的一部分。
原文连接:http://msdn.microsoft.com/zh-cn/magazine/ee309515.aspx#