Visual C++ 2015中的内存分析

[原文发表地址] Memory Profiling in Visual C++ 2015

[原文发表时间] 2015/10/21 3:33PM

正如在之前的博客中公布的,Visual Studio 2015中发布了一系列新的内存分析工具,帮助定位和解决您应用程序中的内存问题。这个新的调试实时分析器在您的调试阶段运行,您可以在任何时间捕获快照,例如在一个断点上,您也可以在任何暂停状态查看堆内容。当您在想要分析您的应用程序堆状态时,此工具提供更精确和灵活的体验。

准备开始

在VS2015中第一次启动调试器时,呈现在您面前的是新的诊断工具窗口,它能让您看到您正运行的应用程序调试器事件、内存使用率,以及CPU利用率。您可利用内存使用率图来监控调试过程中的总体内存消耗,当您需要更多信息时,您也可以开启堆数据分析并捕获快照来得到更为详细的内存分配分析。

内存分配跟踪及总览

内存分析器可以在应用程序运行中实时收集分配事件数据,然后将这些数据以类型信息形式映射到您的PDB中。由于对VS2015 (v140) 编译器的技术性依赖,类型信息只显示那些使用此版本或更高版本编译器来编译的目标。在一个应用程序调试的过程中收集内存分配事件数据是有内在开销的,因此快照功能默认是关闭的。如要激活堆快照,在诊断工具窗口中打开内存使用率分页并点击堆分析按钮,按钮图标就变为黄色。自VS2015 Update 1 CTP预览版起,堆分析的开启可以无需重启调试器。发生在开启快照前的分配将不会被跟踪,因此您要确保在开启堆分析之后您有一个合适的基线可以重启调试器,或者在设计阶段按F11来开始调试和跳出第一条指令。注意如果您关闭堆快照功能,此项设置将会在终止现有的调试会话之后起作用。

捕获快照

一旦开启快照功能,快照将会在点击“捕获快照”按钮时获取并显示在快照表中。每张快照列出了捕获快照时的执行时间、分配总数和千字节的堆大小。您可以通过点击每一列(内存分配数或堆大小)左边的总数链接打开这个堆快照的完整视图。选择每一列右边的+/-链接可以看到快照差异,并且打开的快照是以此数值排序显示的。

堆检查

类型视图

当您打开一张快照,最初是以在内存中包含的所有对象类型列表显示的。每一个数据类型的总数和内存占用是以降序默认列出的。您可以通过点击相应列的顶部单元格来实现以对象类型名称、数量或大小来排序。在类型视图中不确定类型是默认隐藏的,但是它可以通过点击查询框左边的过滤器图标并取消“隐藏不确定类型”被轻松地显示。

实例视图

双击或者右击一行,选择一个类型“查看实例”,您就可以导航到这个类型的实例并查看单个对象以及它们完整的分配调用堆栈。选择调用堆栈的一行,它将链接到对应的源代码上。您可以通过悬停在一行实例上来激活调试数据提示,这样就能很方便地查看对象的内容,如下在CTrackerChannel 这个实例上的演示:

堆栈视图

在类型页中视图选择框中选择“堆栈”,就可以轻松地通过堆栈帧查看调用树了。您可以通过选择窗口右上角的按钮来根据调用和被调用收集堆栈帧。所有的分配都源于被标记为[self]的当前函数。您可以用右上角的搜索框搜索调用堆栈,很容易地就会搜索到您想找的帧:

从调用树中选择一个堆栈帧,源于此堆栈帧的所有分配和他们的类型就会出现在窗口下部的面板中。展开分配,它的分配调用堆栈将显示在下面。

支持定制分配器

发布的Windows 附带一个ETW提供者,它能在Windows堆中进行事件跟踪分配和重新分配。我们可以把提供者配置成:每个事件包含一个堆栈跟踪。标签__declspec(allocator)可以使编译器确定一个函数调用是对一个分配器的调用,也就是说,一个函数返回新的堆分配内存。在每一次调用分配器函数时,调用点的地址、调用指令的大小和新对象的typeid发送到一个包含在PDB文件里的新的S_HEAPALLOCSITE符号 。当Windows堆代码对一个分配发出一个带有调用堆栈的ETW事件时 ,内存工具在调用堆栈中寻找一个与S_HEAPALLOCSITE符号相匹配的返回地址。符号中的类型typeid决定了分配的运行时类型。

在CRT(新建,分配内存,……)和Windows SDK中的分配器已经在源代码层被标记,这样它们的分配数据可以被捕获并映射到相应的符号。 任何代码想要支持在堆分析期间显示分配的类型信息, 需要v140编译器(或者更高的版本)来编译 。为了提高内存分析的准确性,可以利用__declspec(allocator),确保任何函数返回一个指向新分配的堆内存的指针,正如 myMalloc(size_t size)这个例子:

__declspec(allocator) void* myMalloc(size_t size);

VS2015 Update 1 CTP上的新功能

支持 附加到进程 分析

Visual Studio 2015 Update 1 CTP版本现在具备附加到一个正在运行的进程以及激活对那个进程进行本地堆分析的能力。如果您怀疑一个内存泄漏是在执行后期发生的,在您调试程序问题之前,您可以回避分配跟踪的性能损失的问题。在一个正在运行的进程上激活堆分析的进程同以上一样。一旦分析被激活,这一设置将贯穿于整个目标进程的调试会话始终。

支持远程分析

Visual Studio 的CTP版本同样具备当远程调试一个应用程序时,分析这个应用程序,甚至附加到远程进程分析的能力。这使您可以在超越您开发环境之外的设备上,进行内存使用情况的监控,就像在本地调试会话中一样捕获堆快照。