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

And*_*ell 1 c# memory-leaks loops

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

主方法的循环段:

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

方法叫:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

Jim*_*hel 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);
            }
Run Code Online (Sandbox Code Playgroud)

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

它还使代码更易于阅读.

也就是说,我认为你的"内存泄漏"可能只是GC在收集内存方面花费了很多时间.

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

while (true)
{
    listMessages = FetchAllMessages();
    Thread.Sleep(5000);
}
Run Code Online (Sandbox Code Playgroud)

你正在占用一个花费大部分时间无所事事的线程.你最好创建一个间隔为5秒的计时器,如下所示:

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

// Do this in your initialization
MailTimer = new Timer(MessageFetcher, null, 5000, -1);
Run Code Online (Sandbox Code Playgroud)

而你的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);
}
Run Code Online (Sandbox Code Playgroud)

初始化创建一个单次计时器,在五秒钟内到期并调用MessageFetcher.当MessageFetcher完成时,它设置了一个定时器,使得邮件将另5秒内检查.你想这样做而不是设置一个周期性的间隔,因为你不希望定时器MessageFetcher在前一个滴答未完成处理时再次调用.

该MessageFetcher方法在池线程上执行.使用计时器可以防止你不得不一直保持一个线程,占用内存,而它什么也没做.