C# Winforms应用程序中总进程内存使用情况报告矛盾

10

编辑:悬赏已过期,但如果社区想要将其授予某个人,那么我选择Raful Chizkiyahu。


我在我的一个C# Winforms程序中有一个内存泄漏,并且我想绘制其随时间变化的内存使用情况,以更好地了解可能导致泄漏的原因。问题是,没有任何通常的内存诊断命令与任务管理器声称的该进程的消耗内存相匹配。我认为这可能是应用程序使用不安全/非托管代码未包括在总内存中的原因。

因此,为了深入钻研,我创建了一个新的Winforms应用程序,非常简单,只有一个计时器实时报告内存使用情况。我使用5个标签,每个标签都有各种函数(大多数来自于这里):Environment.WorkingSetGC.GetTotalMemory(false)GC.GetTotalMemory(true)Process.GetCurrentProcess().PrivateMemorySize64Process.GetCurrentProcess().WorkingSet64

令人惊讶的是(或者也许不是,叹息),我仍然无法使这五个数字中的任何一个与Windows 10任务管理器匹配。这里是一张截图:

pic

因此,我基本上正在寻找的是那个5.1MB数字。如何从.NET框架中巧妙提取该隐藏数字?

这是我在计时器tick函数中的代码:

private void timer1_Tick(object sender, EventArgs e)
{
    //Refresh();
    label1.Text = "Environment.WorkingSet: " +  Environment.WorkingSet ;
    label2.Text = "GC.GetTotalMemory(false): " +    GC.GetTotalMemory(false) ;
    label3.Text = "GC.GetTotalMemory(true): " + GC.GetTotalMemory(true) ;
    Process proc = Process.GetCurrentProcess();
    label4.Text = "proc.PrivateMemorySize64: " +    proc.PrivateMemorySize64 ;
    label5.Text = "proc.WorkingSet64: " +       proc.WorkingSet64 ;
    proc.Dispose();
}

可能很明显,我尝试过使用Refresh()命令和不使用该命令,但都没有成功。


编辑:悬赏已到期,但如果社区愿意授予悬赏给某个人,那么我选择Raful Chizkiyahu。


2
做自己一个忙,不要太关注任务管理器,它不可靠。看看dotMemory或尝试使用Visual Studio的性能分析器或sysinternal工具或类似工具。另请参阅https://dev59.com/xXVC5IYBdhLWcg3w9GLM。 - George Vovos
虽然与您的问题无直接关系,但如果您遇到的确实是托管内存泄漏,这篇文章将向您展示如何使用windbg和SOS扩展来确定泄漏源。我已经两次应用了这种技术,在两种情况下都能够诊断出问题。 - BlueStrat
4个回答

6
Windows任务管理器只显示实际存储在RAM中的内存,进程内存包括磁盘上的页面,这就是进程内存可能比您机器上实际RAM更大的原因。

如果是真的,那么微软这样做非常具有误导性。我很难相信他们会以这种方式限制报告,因为提供总数或单个分页/ RAM使用量是非常简单的事情。 - Dan W
在任务管理器中,您可以查看每个系统的已提交、缓存和分页池。如果您想要了解每个应用程序使用了多少内存,您可以查看应用程序从系统请求了多少内存,而不是系统保存内存的位置(L1、L2、L3、RAM磁盘)。 - Raful Chizkiyahu
啊!你是指“详细信息”选项卡吗?如果是的话,能否在你的答案中添加一下呢?看起来我可以添加更多的列,我得到了这个结果。正如你所看到的,在VS中的proc.WorkingSet64数字似乎对应于任务管理器中的“工作集(内存)”。Environment.WorkingSet也很接近,那么从中选择哪个是最好的“真实”内存值,proc.WorkingSet64呢?我也不确定19,364 KB在VS中的等价数字是多少。 - Dan W
我本来打算可能会给你悬赏金的,但只是想等到更接近截止日期。看起来我错过了最后期限。无论如何,你的回答是最早的(而且我认为)最合适的答案。所以这里简单地写个便条来感谢你。 - Dan W

1
我注意到Visual Studio调试工具在大多数情况下报告的内存使用量比任务管理器高,而任务管理器只报告RAM的使用情况。VS中的调试工具会考虑所有内容,如页面文件等,因此我认为它们更可靠。

谢谢,我正在逐渐接近。请看一下我对Raful Chizkiyahu的最新评论。 - Dan W

1
任务管理器不能给出“精确”的RAM使用情况。尝试使用Visual Studio的诊断工具或资源监视器。但是,为了获得精确的内存使用情况,我认为这段代码会起作用。
using System.Diagnostics;
// This obtains the current application process
Process thisProcess = Process.GetCurrentProcess();

// This obtains the memory used by the process
long usedMemory = thisProcess.PrivateMemorySize64;

// This will display the memory used by your C# app
 label6.Text= System.Convert.ToString(usedMemory)

我假设你下一个标签将是"label6"。如果你添加了更多的标签,请更改它。
希望这有所帮助 :)
编辑:如果你将“usedMemory”除以2850023.23,你可以得到一个与任务管理器显示的内存大致相等的值。
// This obtains the current application process
        Process thisProcess = Process.GetCurrentProcess();

        // This obtains the memory used by the process
        long usedMemory = thisProcess.PrivateMemorySize64;

        // 3. This will display the memory used by your C# app
        label6.Text = System.Convert.ToString(usedMemory/ 2850023.23);

这看起来就像是我在主贴中使用label4的输出。我尝试了你的建议,将其除以2850023.23(这个数字有什么特别之处?),我得到了label6的8.3MB和任务管理器中的5.3MB,所以它看起来比我们想象的更棘手。 - Dan W
我刚才把显示的内存除以了任务管理器中显示的内存哈哈。我以为它是一个固定的常数。如果不起作用,很抱歉。 - Vinayak9769
哈哈,不错的尝试 ;) - Dan W

0

你需要更好的工具支持,TaskManager 只能在某个时刻给出近似的内存使用情况。

如果你怀疑存在内存泄漏,最可靠的方法是对应用程序进行内存转储(快照)。

应该在应用程序启动时拍摄一张快照,然后在随后的时间间隔内再拍摄。如果内存上升缓慢,则需要在每个快照之间等待一段时间。然后可以比较这些快照。这种方法允许你分析和比较对象分配、对象计数,甚至非托管内存。

Microsoft已经为集成到Visual Studio中的Memory Usage工具编写了一份指南。在大多数情况下,这应该足以识别泄漏。

如果你发现集成的工具受限,可以查看SO问题和侧边栏中链接的其他内容。


谢谢,这很有用,但首先我需要跟踪哪种类型的内存统计信息(在十个左右中选择一个),然后从那里查看内存使用情况及其发生的情况(它不容易重现,并且有时会发生内存泄漏)。所以首先,我将编写一个简单的日志记录器,并尝试追踪内存泄漏开始出现的模式。 - Dan W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接