每次运行代码段时如何停止常量内存增加?

时间:2013-08-08 22:50:33

标签: c# loops memory-leaks

我有一个程序在后台每5秒运行一些方法。但是每5秒钟它的物理内存使用量会增加16-20 Kb。通过评论代码段,我将其缩小到这个特定的部分是造成问题的原因。我在这里错过了什么来正确释放分配的内存?

主方法的循环段:

    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;
        }
    }

2 个答案:

答案 0 :(得分: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方法在池线程上执行。使用计时器可以防止你不得不一直保持一个线程,占用内存,而它什么也没做。

答案 1 :(得分:0)

正如Paddy所说,最终垃圾收集将绕过处理对象和释放内存,但你可以手动强制它,尽管通常更好地允许它自动发生。

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

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

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

  谁知道?这不是确定性的。可以这样想:在一个系统上   具有无限的内存,垃圾收集器不必这样做   任何东西。而你可能会认为这是一个不好的例子,但那是什么   垃圾收集器正在模拟你:一个无限的系统   记忆。因为在具有足够可用内存的系统上   比你的程序要求,垃圾收集器永远不必运行。   因此,您的程序无法对何时做出任何假设   记忆将(如果有的话)被收集。

     

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

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