1

我有一个程序在后台每 5 秒运行一些方法。然而,每 5 秒,它的物理内存使用量就会增加 16-20 Kb。通过注释掉代码段,我将其缩小到这个特定的段是导致问题的原因。我在这里缺少什么来正确释放分配的内存?

来自 main 方法的循环段:

    while (true)
    {
        listMessages = FetchAllMessages();
        //Commented out other segments. Not causing memory increase
        System.Threading.Thread.Sleep(5000);
    }

方法调用:

    public static List<Message> FetchAllMessages()
    {
        try
        {
            using (Pop3Client client = new Pop3Client())
            {
                client.Connect("pop.gmail.com", 995, true);
                client.Authenticate("removed", "removed");
                int messageCount = client.GetMessageCount();
                List<Message> allMessages = new List<Message>(messageCount);

                for (int i = messageCount; i > 0; i--)
                {
                    if (verifiedEmail.Contains(client.GetMessage(i).Headers.From.Address) || verifiedSms.Contains(client.GetMessage(i).Headers.From.Address))
                    {
                        string tempMessage = client.GetMessage(i).ToMailMessage().Body.ToLower();
                        if (tempMessage.Contains("cmd") && tempMessage.Contains("fin"))
                        {
                            allMessages.Add(client.GetMessage(i));
                        }
                    }

                    client.DeleteMessage(i);
                }
                client.Disconnect();
                return allMessages;
            }
        }
        catch (Exception ex)
        {
            return null;
        }
    }
4

2 回答 2

5

可能导致内存使用量稳步增加的一件事是您调用GetMessage了很多次。根据您的 POP 客户端的编写方式,可能每次都分配一个新缓冲区,以便它可以从 POP 服务器下载消息。最终当然会收集该内存,但是您正在不必要地使用垃圾收集器。而且你的效率也很低。

您应该考虑将代码更改为以下内容:

            for (int i = messageCount; i > 0; i--)
            {
                var msg = client.GetMessage(i);
                if (verifiedEmail.Contains(msg.Headers.From.Address) 
                    || verifiedSms.Contains(msg.Headers.From.Address))
                {
                    string tempMessage = msg.ToMailMessage().Body.ToLower();
                    if (tempMessage.Contains("cmd") && tempMessage.Contains("fin"))
                    {
                        allMessages.Add(msg);
                    }
                }

                client.DeleteMessage(i);
            }

因此,您只需调用一次,而不是调用client.GetMessage(i)四次。

它还使代码更易于阅读。

也就是说,我认为您的“内存泄漏”很可能只是 GC 在收集内存方面花费了自己的甜蜜时间。

另一件事。你有一个睡眠循环:

while (true)
{
    listMessages = FetchAllMessages();
    Thread.Sleep(5000);
}

您正在绑定一个大部分时间什么都不做的线程。您最好创建一个间隔为 5 秒的计时器,如下所示:

System.Threading.Timer MailTimer; // declare at class scope

// Do this in your initialization
MailTimer = new Timer(MessageFetcher, null, 5000, -1);

你的 MessageFetcher 方法是:

void MessageFetcher(object state)
{
    listMessages = FetchAllMessages();
    // do that other stuff that you didn't show

    // reset the timer so that it fires 5 seconds from now
    MailTimer.Change(5000, -1);
}

初始化会创建一个在五秒后到期的一次性计时器并调用MessageFetcher. 完成MessageFetcher后,它会设置一个计时器,以便在五秒钟内检查邮件。MessageFetcher您希望这样做而不是设置周期性间隔,因为如果前一个滴答未完成处理,您不希望计时器再次调用。

MessageFetcher方法在池线程上执行。使用计时器可以防止您必须始终保持线程,在它基本上什么都不做时占用内存。

于 2013-08-09T18:04:54.417 回答
0

正如 Paddy 所说,最终垃圾收集会处理对象和释放内存,但您可以手动强制它,尽管通常最好让它自动发生。

但是要测试垃圾收集会减少内存,请While在几次调用后退出循环并调用GC.Collect();. 内存应该下降。

调用GC.Collect();很昂贵,这就是为什么你最好让操作系统选择自动调用垃圾收集的最佳时间。

这是对有关您关注的类似问题的一个很好的答案:C# Garbage collection

谁知道?这不是确定性的。可以这样想:在内存无限的系统上,垃圾收集器不需要做任何事情。你可能认为这是一个不好的例子,但这就是垃圾收集器为你模拟的:一个具有无限内存的系统。因为在可用内存比程序所需的足够多的系统上,垃圾收集器永远不必运行。因此,您的程序无法对何时收集内存(如果有的话)做出任何假设。

所以,你的问题的答案是:我们不知道。

我建议设置一个内存分析器来记录应用程序的内存消耗并运行一段时间进行测试。您应该看到垃圾收集器将自动控制一切,而无需您对代码进行任何更改。

于 2013-08-08T23:08:52.133 回答