VS 2014 Preview 中的本地内存诊断

[原文发表地址]Native Memory Diagnostics in VS2015 Preview

[原文发表时间]2014/11/21 7:19PM

在Visual Studio 2013 Update 2 和早期发布的VisualStudio 2015, CTP, 我们发布了一个内存诊断工具, 这个工具允许开发者截取应用程序的堆快照, 然后在终止它研究堆内容。在最初发布的版本中,支持查看堆管理和本地对象。并且在第一个Visual Studio2015 update CTP中增加了支持本机类型推导和值检验。

虽然这个工具是提供VisualStudio开发者含有一个收件箱内存分析器的一个良好开端, 但是在特定的程序状态下, 它缺乏一种轻松的分析堆内容的能力。因为为了更深层的研究数据,整个程序必须关闭。

改进的内存分析器进行预览

现在在Preview上,有一个新改进的内存分析器是可用的。它允许开发人员利用调试器强大的程序流程的控制力,并且在任何破发状态可以检验他们应用程序的堆内容。这是一个很好的对新的内存分析总结经验的一个概述。一个深入的特性总结完成了指令的激活功能。(我在那里找到它?章节), 在下面的这些指令是在第一次激活这个工具。 在调试期间,简单的按下F5将会启动新的分析器。现在为了查看堆快照不需要去终止程序。

这篇文章的剩余部分将集中介绍利用本机程序的新工具和详细介绍该工具的工作流程细节。

演示:分析 a Native MFC App

要展示新的内存分析器,一个被称为FamiTracker的MFC开源芯片定序器已经被加载到Visual Studio,并且为了建立新的编译器做了简单的修改。在启动内存分析器后,在应用程序中通过注册密钥和启动调试场景利用F5,工具加载。很快内存使用情况将被显示,并且下面的堆快照也将被显示:

 

快照可以在不同的点及时地的捕获堆状态,例如值仅可见于最近拍照时和处于破发状态时。

在这次练习中,对于FamiTracker,初始的程序状态是初始化序列UI:

                                             FamiTracker 初始化编曲界面

另一个称为编辑器工具窗口会被打开为了编辑每一个编辑器的属性:

                                          FamiTracker 编辑器窗口

利用新的内存分析器,为了更好的理解应用程序的内存消耗,我们将采取堆快照跨越两种程序状态。

首先,我们截取一个基本的快照为了储存初始化的堆内容:

工具编辑框被打开,这将在代码中触发一个断点,并且在程序状态中开始了一个变化,这个函数初始化工具编辑对话框和激活其它的一些辅助功能, 这将有助于创建工具编辑器的UI。

OnInitDialog() 开始断点的位置开始截取快照, 我们看到应用程序的堆内容之前,工具编辑器对话框启动分配对象。快照将列出对象类型,数量和内存占用。

由于我们在休息状态下, 通过双击某一行或者图标:

选择一个类型可以看到针对每一个实例的所有类型的分配情况,完整值和分配的调用堆栈。下面是所有CCHannelHandlerN163[]的实例:

 

通过一些断点后继续,工具编辑器对话框终于弹出,并且拍取第二张快照。我们可以看到整个工具对话框调用期间的所有的内存消耗数量,
在这种情况下,稍微超出50千字节。

51227字节以及405的分配揭示了通过启动仪器编辑相对于基线快照#1的对话所消耗的总的额外附加内存,并且顶部的数字显示的是总的堆栈内容,点击其中任意一个自上次拍照已经存在其它不同的差异快照, 将看到堆快照的所有对象,在快照中只需要单击两个顶部详情的任意一个。以下显示的是第二个与第一个的不同:

我们甚至可以通过将断点设在一个特殊函数的开始和结束的位置 和采用两个快照做比较,来更精确的检查内存的开销。在仪器对话框条用期间函数“InsertPane”调用两次,所以很好的看到它在整个调用期间的具体影响力。 两个断点设置在封装的函数InsertPane,并且每个快照都会击破,下面是第二个快照的详细信息, 我们可以很清楚的看到,这个函数利用了20行代码大约12KB。

 快照的差异技术允许你代码的任何区域, 为了分析内存占用和为揭露执行状态之间可能发生的内存泄露的强有力的工具。一个拍取两个快照,那第二个快照将会自动显示与第一个的差异。对于差异非连续拍照,只需点击快照的右上角和选择一个与先前不同的快照进行差异比较。

通过点击已打开快照的右上方的比较组合框选择所要的快照进行比较。

"Hide Undetermined Types" 视图设置

由于工具的类型特性是由PDB派生的,一些类型不能被确定由于缺乏符号或者由于使用定制的分配器,我们计划在未来的博客文章中为自定义分配显示扩张性模型。因为他对检查用户定义类型与可用的符号是很重要的,也不妨碍工作流程。我们选择了在默认情况下隐藏这些对象。

这些不确定的对象可以 很容易的通过选择在堆试图顶部的试图设置下拉框中取消选中选项看出来。

在堆栈列表中将会显示“Undetermined”类型列表,反过来就会显示内存中的所有实例, 完成分配的调用堆栈。下面是在地址<0x1E1148>中的实例表示:

 

在没有任何启用确定类型和默认隐藏设置的情况下,堆表中将会显示出下面的字符串水印:

已知问题:

正如查尔斯博客中提到的,目前支持本机应用类型的是Win32, MFC和windows商店的应用程序。所有的C++工程必须使用新版的Visual Studio2015(V140)编译器使用此工作进行编译正常工作, 以下的场景是不支持的:

  • 64位目标
  • 所有工程类型的远程调试
  • 连接到过程

结束语

这是一个令人兴奋的新功能的早期版本,请分享你的想法,并帮助我们致使这个对你的内存诊断需求有价值和强大的工具!