如何在每次运行代码段时停止不断增加的内存

本文关键字:段时停 不断增加 内存 代码 运行 | 更新日期: 2023-09-27 18:26:53

我有一个程序,它每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;
        }
    }

如何在每次运行代码段时停止不断增加的内存

可能导致内存使用量稳步增加的原因之一是您多次调用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方法在池线程上执行。使用计时器可以防止线程一直处于活动状态,占用内存,而线程基本上什么都不做。

正如Paddy所说,垃圾收集最终会处理对象并释放内存,但您可以手动强制执行,尽管通常最好允许自动执行。

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

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

这是关于您关注的类似问题的一个很好的答案:C#垃圾回收

谁知道呢?这不是确定性的。这样想:在一个系统上有了无限内存,垃圾收集器就不必做任何东西你可能会认为这是一个糟糕的例子,但事实就是这样垃圾收集器正在为您模拟:一个具有无限记忆力因为在具有足够多可用内存的系统上与程序所需的相比,垃圾收集器永远不需要运行。因此,您的程序无法对何时记忆将会(如果有的话)被收集起来。

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

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