为啥“查看堆”结果与 Visual Studio 中的“进程内存使用”不匹配

Posted

技术标签:

【中文标题】为啥“查看堆”结果与 Visual Studio 中的“进程内存使用”不匹配【英文标题】:Why the "View Heap" result does not match with 'Process Memory Usage' in Visual Studio为什么“查看堆”结果与 Visual Studio 中的“进程内存使用”不匹配 【发布时间】:2015-08-16 18:14:27 【问题描述】:

我正在尝试使用 Visual Studio 来跟踪我的应用程序中的内存使用情况。在“诊断工具”窗口中,它显示我的应用程序正在使用 423 MB。谢谢我去'内存使用'和'ViewHeap',当我点击快照时,我得到一个我的对象大小的表。

但是当我把这些数字加起来时:

= 3317228 
+  403764
+  354832
+  264984
+  244836
+  195748
+  144032
+   28840
+   16452
+   13920
+   13888
+    3428
+    2100
+      20
= 5004072
= 4.77 MB

我的问题是为什么这个数字 4.77MB 与我在“内存”图表上看到的 423MB 不匹配。我希望看到左边的表格详细说明了 423 MB 的去向。请告诉我我错过了什么?

【问题讨论】:

这些值(至少在 Visual Studio 2015 RC 中)对于进程占用的特定内存都不是特别准确。它们都旨在为您提供一种简单的方式来检测应用程序中的潜在内存问题,不一定用于性能基准测试。如果您查看任务管理器,那里分配的值可能与这些值中的 both 不同。例如,图表中有 21.5MB,堆中有 92.3KB,任务管理器中有 4,200KB 用于一个特定的应用程序。 谢谢。但我计算出的数字是 4.77MB,与“内存”图上的数字 (423MB) 相差甚远。它关闭了 10 倍。这也正常吗? 我的 (92.3KB) 减少了 233%。我相信您应该使用 start diagnostic tools without debugging 选项。当使用F5 调试器与诊断工具一起工作时,调试工具会出现大量开销。此外,任何非共享​​>数据(即加载的类型等)都将显示在Memory (MB) 图表中,因为它是私有进程内存。 【参考方案1】:

该图显示了整个进程的私有字节数。这包括托管堆、本机堆、堆栈等...有关内存指标类型的更多信息,请参阅此答案:What is private bytes, virtual bytes, working set?

堆视图表仅显示拍摄快照时托管堆上的活动类型(不能被垃圾回收)。要查看本机堆和托管堆上的类型,请切换到混合模式调试。堆视图(以及快照表中的数字)是图中显示的进程内存的子集。

集成调试器的工具最适合尝试找出内存意外增长的原因,或追踪本应被垃圾回收但仍有引用保持其活动状态的对象。

这是我在 Memory 工具上写的(我为 MSFT 工作)的博客文章,解释了如何使用过时的引用来追踪对象: https://web.archive.org/web/20150905153352/http://blogs.msdn.com/b/visualstudioalm/archive/2015/04/29/diagnosing-event-handler-leaks-with-the-memory-usage-tool-in-visual-studio-2015.aspx

【讨论】:

【参考方案2】:

为什么 View Heap 大小与内存图表大小不匹配?

造成这种情况的潜在原因有很多,包括 JITter调试工具调试符号只是我的代码 strong>、垃圾收集等。我们将介绍两个大的。

只是我的代码

Visual Studio 的只是我的代码 功能倾向于隐藏分配、异常、断点和任何其他非代码元数据对用户来说,这不是从.PDB 文件或打开的项目中加载。详情请见MSDN Just My Code。

调试符号和工具

在 Visual Studio 中调试任何项目时,Visual Studio 调试器会运行并分配额外内存以允许断点异常捕获和其他特性。对于true 诊断工具捕获,您应该使用Alt+F2 选项,或Debug > Start Diagnostic Tools without Debugging...。您还需要为此部分切换到发布模式。仅此步骤就将图表显示的内存(对我而言)从 21.5MiB 减少到 5.5MiB,表明 调试符号调试工具实质性的 因素。请记住,为了让 Visual Studio 能够捕获异常、断点和其他数据,它必须将自己附加到您的进程以及进程中的所有对象。

那么,我们如何使这些数字匹配?

你真的不应该担心数字匹配。 Memory Graph 和 View Heap 图表的目的是让您看到峰值和奇怪的内存波动,这可能表明程序不正确。您应该寻找那些,而不是关注两个值之间的差异。

也就是说,您可以采取一些步骤来获得准确的结果。

真正匹配数字

如果您真的想要匹配它们,我认为它不能以您希望的方式完成。但是,您可以靠近。第一步是Start Diagnostic Tools without Debugging...,然后选择Memory Usage。选择后,点击其旁边的 Settings Gear,并确保 Profiler TypeMixed (Managed and Native)。然后,单击开始并拍摄一些快照,以便检查内存使用情况。完成后,停止调试并检查内存。

要检查您的记忆,请在快照框中单击您要检查的快照的左上角蓝色数字。在此页面上,单击 右上角 上的 网格图标 并取消选择 Just My CodeCollapse Small Objects。切换到 Native Heap 选项卡并执行相同操作,取消选择 Just My Code,然后选择 Include Freed Allocations

您应该会发现,仅此一项就可以使您的错误更接近实际值。 (实际值是 Private Bytes,错误是 Heap Size)在我测试的应用程序中,它使总数(来自两个堆)大约为 @ 987654330@,这与我在 Visual Studio 之外运行程序时 Task Manager 指示的分配大致相同(这个实际值是1.1211MiB,但是数字这么小,误差范围是预期)。

包括释放的分配是什么意思?本质上,当GC 清除内存时,该内存不会立即从应用程序空间中删除。相反,它被释放以供其他对象使用,但仍可以保留在应用程序中。 Garbage Collection 是一个复杂的话题,远远超出了本问答的范围。

附加说明

内存分配、使用和测量是一个非常复杂的话题。不幸的是,处理这种情况的 100% 万无一失的方法并不多,而且通常越是万无一失和准确的解决方案,它就越复杂、缓慢且难以使用。

参考文献

MSDN 只是我的代码:https://msdn.microsoft.com/en-us/library/dn457346.aspx#BKMK__NET_Framework_Just_My_Code

MSDN 垃圾回收:https://msdn.microsoft.com/en-us/library/0xy59wtx%28v=vs.110%29.aspx

此答案的其余部分基于我自己的实验反复试验,可能会因不同的环境而导致潜在的不准确性。此处介绍的步骤可能适用于所有开发人员,并且是使用 Visual Studio 2015 RC 版本 14.0.22823.1 D14REL 执行的。

【讨论】:

以上是关于为啥“查看堆”结果与 Visual Studio 中的“进程内存使用”不匹配的主要内容,如果未能解决你的问题,请参考以下文章

为啥“if constexpr”不能与 Visual Studio 2017 15.3 一起编译?

为啥发布运行的代码与在 Visual Studio 中运行不同

Typescript:为啥 Visual Studio 代码不报告与命令行 tsc 相同的错误?

为啥我的项目没有从 Visual Studio 上传到 github?

c++ 函数模板的问题,请看代码,为啥去掉注释后,visual studio 2012会无法运行。

为啥 Visual Studio Code 想要访问我的私有 SSH 密钥?