c# - 用于发送电子邮件的 C# Windows 服务中的内存泄漏

标签 c# windows memory-leaks service smtpclient

最近这似乎是一个非常流行的问题/问题,但我似乎找不到解决问题的办法。

我用 C# 创建了一个简单的 Windows 服务来发送电子邮件。该应用程序运行良好,除了它的内存使用。该应用程序的前端是基于网络的,服务通过在目录中创建的文本文件排队。读取文本文件后,该服务从 MS SQL 数据库收集时事通讯信息和电子邮件地址,并开始每 4 秒发送一封电子邮件。通过任务管理器观察服务运行时,您可以看到 CPU 使用率每 4 秒上升一次,但立即回落。另一方面,内存似乎不是每封电子邮件都增加了 50-75k,而是每 3-4 封电子邮件增加了 50-75k。这将继续增加,直到发送完所有电子邮件。我刚发出大约。 2100 封电子邮件,内存使用量高达 100MB。我注意到的另一件事是,在发送完所有电子邮件后,内存使用量将保持在这个总数,直到我重新启动服务。当服务空闲时,内存运行在 6500k 左右。有人对我如何在邮件完成后减少和处理内存使用有任何建议吗?我的代码如下。任何帮助将不胜感激..

namespace NewsMailer
{
    public partial class NewsMailer : ServiceBase
    {
        private FileSystemWatcher dirWatcher;
        private static string filePath = @"E:\Intranets\Internal\Newsletter\EmailQueue";
        private static string attachPath = @"E:\Intranets\Internal\Newsletter\Attachments";
        private string newsType = String.Empty;
        private string newsSubject = String.Empty;
        private string newsContent = String.Empty;
        private string userName = String.Empty;
        private string newsAttachment = "";
        private int newsID = 0;
        private int emailSent = 0;
        private int emailError = 0;

        public NewsMailer()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            dirWatcher = new FileSystemWatcher();
            dirWatcher.Path = filePath;
            dirWatcher.Created += new FileSystemEventHandler(ReadText);
            dirWatcher.EnableRaisingEvents = true;
        }

        protected override void OnStop()
        {
            dirWatcher.EnableRaisingEvents = false;
            dirWatcher.Dispose();
        }

        private void ClearVar()
        {
            newsType = String.Empty;
            newsSubject = String.Empty;
            newsContent = String.Empty;
            userName = String.Empty;
            newsAttachment = "";
            newsID = 0;
            emailSent = 0;
            emailError = 0;
        }

        private void ReadText(object sender, FileSystemEventArgs e)
        {
            ClearVar();
            SetLimits();
            string txtFile = filePath + @"\QueueEmail.txt";
            StreamReader sr = new StreamReader(txtFile);
            string txtLine = String.Empty;

            try
            {
                while ((txtLine = sr.ReadLine()) != null)
                {
                    string[] lineCpl = txtLine.Split('§');
                    newsType = lineCpl[0];
                    userName = lineCpl[1];
                    newsID = Convert.ToInt32(lineCpl[2]);
                }
            }
            catch (IOException ex)
            {
                SendExByMail("ReadText() IO Error", ex);
            }
            catch (Exception ex)
            {
                SendExByMail("ReadText() General Error", ex);
            }
            finally
            {
                sr.Close();
                sr.Dispose();
            }
            GetNews();
        }

        [DllImport("kernel32.dll")]
        public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);

        private void SetLimits()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();

            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);

        }

        private void DeleteText()
        {
            try
            {
                File.Delete(filePath + @"\QueueEmail.txt");
            }
            catch (IOException ex)
            {
                SendExByMail("DeleteText() IO Error", ex);
            }
            catch (Exception ex)
            {
                SendExByMail("DeleteText() General Error", ex);
            }
        }

        private void GetNews()
        {
            string connectionString = ConfigurationManager.ConnectionStrings["contacts"].ConnectionString;
            SqlConnection conn = new SqlConnection(connectionString);

            string sqlSELECT = "SELECT newsSubject, newsContents, username, attachment FROM newsArchive " +
                               "WHERE ID = " + newsID;

            SqlCommand comm = new SqlCommand(sqlSELECT, conn);

            try
            {
                conn.Open();
                using (SqlDataReader reader = comm.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        newsSubject = reader[0].ToString();
                        newsContent = reader[1].ToString();
                        userName = reader[2].ToString();
                        newsAttachment = reader[3].ToString();
                    }
                    reader.Dispose();
                }
            }
            catch (SqlException ex)
            {
                SendExByMail("GetNews() SQL Error", ex);
            }
            catch (Exception ex)
            {
                SendExByMail("GetNews() General Error", ex);
            }
            finally
            {
                comm.Dispose();
                conn.Dispose();
            }
            DeleteText();
            GetAddress();
        }

        private void GetAddress()
        {
            string connectionString = ConfigurationManager.ConnectionStrings["contacts"].ConnectionString;
            SqlConnection conn = new SqlConnection(connectionString);

            string sqlSELECT = String.Empty;
            if (newsType == "custom")
                sqlSELECT = "SELECT DISTINCT email FROM custom";
            else
                sqlSELECT = "SELECT DISTINCT email FROM contactsMain WHERE queued = 'True'";

            SqlCommand comm = new SqlCommand(sqlSELECT, conn);

            try
            {
                conn.Open();
                using (SqlDataReader reader = comm.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        try
                        {
                            if (CheckEmail(reader[0].ToString()) == true)
                            {
                                SendNews(reader[0].ToString());
                                Thread.Sleep(4000);
                                emailSent++;
                            }
                            else
                            {
                                SendInvalid(reader[0].ToString());
                                emailError++;
                            }
                        }
                        catch (SmtpException ex)
                        {
                            SendExByMail("NewsLetter Smtp Error", reader[0].ToString(), ex);
                            emailError++;
                        }
                        catch (Exception ex)
                        {
                            SendExByMail("Send NewsLetter General Error", reader[0].ToString(), ex);
                            emailError++;
                        }
                        finally
                        {
                            UnqueueEmail(reader[0].ToString());
                        }

                    }
                    reader.Dispose();
                }
            }
            catch (SqlException ex)
            {
                SendExByMail("GetAddress() SQL Error", ex);
            }
            catch (Exception ex)
            {
                SendExByMail("GetAddress() General Error", ex);
            }
            finally
            {
                comm.Dispose();
                conn.Dispose();
            }

            SendConfirmation();
        }

        private bool CheckEmail(string emailAddy)
        {
            bool returnValue = false;
            string regExpress = @"^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$";

            Match verifyE = Regex.Match(emailAddy, regExpress);
            if (verifyE.Success)
                returnValue = true;
            return returnValue;
        }

        private void SendNews(string emailAddy)
        {
            string today = DateTime.Today.ToString("MMMM d, yyyy");

            using (MailMessage message = new MailMessage())
            {
                SmtpClient smtpClient = new SmtpClient();

                MailAddress fromAddress = new MailAddress("");

                message.From = fromAddress;
                message.To.Add(emailAddy);
                message.Subject = newsSubject;

                if (newsAttachment != "")
                {
                    Attachment wusaAttach = new Attachment(attachPath + newsAttachment);
                    message.Attachments.Add(wusaAttach);
                }

                message.IsBodyHtml = true;
                #region Message Body
                message.Body = "";
                #endregion

                smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                smtpClient.Host = "";
                smtpClient.Credentials = new System.Net.NetworkCredential("");

                smtpClient.Send(message);
                smtpClient.ServicePoint.CloseConnectionGroup(smtpClient.ServicePoint.ConnectionName);
            }
        }

        private void UnqueueEmail(string emailAddy)
        {
            string connectionString = ConfigurationManager.ConnectionStrings["contacts"].ConnectionString;
            SqlConnection conn = new SqlConnection(connectionString);
            string sqlStatement = String.Empty;

            if (newsType == "custom")
                sqlStatement = "UPDATE custom SET queued = 'False' WHERE email LIKE '" + emailAddy + "'";
            else
                sqlStatement = "UPDATE contactsMain SET queued = 'False' WHERE email LIKE '" + emailAddy + "'";

            SqlCommand comm = new SqlCommand(sqlStatement, conn);

            try
            {
                conn.Open();
                comm.ExecuteNonQuery();
            }
            catch (SqlException ex)
            {
                SendExByMail("UnqueueEmail() SQL Error", ex);
            }
            catch (Exception ex)
            {
                SendExByMail("UnqueueEmail() General Error", ex);
            }
            finally
            {
                comm.Dispose();
                conn.Dispose();
            }
        }

        private void SendConfirmation()
        {
            SmtpClient smtpClient = new SmtpClient();

            using (MailMessage message = new MailMessage())
            {
                MailAddress fromAddress = new MailAddress("");
                MailAddress toAddress = new MailAddress();

                message.From = fromAddress;
                message.To.Add(toAddress);
                //message.CC.Add(ccAddress);
                message.Subject = "Your Newsletter Mailing Has Completed";
                message.IsBodyHtml = true;
                message.Body = "Total Emails Sent: " + emailSent +
                               "<br />Total Email Errors: " + emailError +
                               "<br />Contact regarding email errors if any were found";

                smtpClient.Host = "";
                smtpClient.Credentials = new System.Net.NetworkCredential("");
                smtpClient.Send(message);
                smtpClient.ServicePoint.CloseConnectionGroup(smtpClient.ServicePoint.ConnectionName);
            }
            ClearVar();
            System.GC.Collect();
        }

        private void SendInvalid(string emailAddy)
        {
            SmtpClient smtpClient = new SmtpClient();

            using (MailMessage message = new MailMessage())
            {
                MailAddress fromAddress = new MailAddress("");
                MailAddress toAddress = new MailAddress("");

                message.From = fromAddress;
                message.To.Add(toAddress);
                //message.CC.Add(ccAddress);
                message.Subject = "Invalid Email Address";
                message.IsBodyHtml = true;
                message.Body = "An invalid email address has been found, please check the following " +
                               "email address:<br />" + emailAddy;

                smtpClient.Host = "";
                smtpClient.Credentials = new System.Net.NetworkCredential("");
                smtpClient.Send(message);
                smtpClient.ServicePoint.CloseConnectionGroup(smtpClient.ServicePoint.ConnectionName);
            }
        }

        private void SendExByMail(string subject, Exception ex)
        {
            SmtpClient smtpClient = new SmtpClient();

            using (MailMessage message = new MailMessage())
            {
                MailAddress fromAddress = new MailAddress("");
                MailAddress toAddress = new MailAddress("");

                message.From = fromAddress;
                message.To.Add(toAddress);
                //message.CC.Add(ccAddress);
                message.Subject = subject;
                message.IsBodyHtml = true;
                message.Body = "An Error Has Occurred: <br />Exception: <br />" + ex.ToString();

                smtpClient.Host = "";
                smtpClient.Credentials = new System.Net.NetworkCredential("");
                smtpClient.Send(message);
                smtpClient.ServicePoint.CloseConnectionGroup(smtpClient.ServicePoint.ConnectionName);
            }
        }

        private void SendExByMail(string subject, string body, Exception ex)
        {
            SmtpClient smtpClient = new SmtpClient();

            using (MailMessage message = new MailMessage())
            {
                MailAddress fromAddress = new MailAddress("", "MailerService");
                MailAddress toAddress = new MailAddress("");

                message.From = fromAddress;
                message.To.Add(toAddress);
                //message.CC.Add(ccAddress);
                message.Subject = subject;
                message.IsBodyHtml = true;
                message.Body = "An Error Has Occurred:<br /><br />" + body + "<br /><br />Exception: <br />" + ex.ToString();

                smtpClient.Host = "";
                smtpClient.Credentials = new System.Net.NetworkCredential("");
                smtpClient.Send(message);
                smtpClient.ServicePoint.CloseConnectionGroup(smtpClient.ServicePoint.ConnectionName);
            }
        }
    }
}

最佳答案

System.Net.Mail.Attachment 实现了 IDisposable,所以我会在其上调用 Dispose()(使用 using ()) 更新:在 Reflector 中打开 MailMessage.Dispose() 会在任何附件上调用 Dispose。

此外,调用 GC.Collect() 实际上会导致大对象堆出现碎片。让框架负责垃圾收集。

你试过下载MemProfiler吗? ? (他们有试用版。通常在使用几分钟内就能收回成本!)

关于c# - 用于发送电子邮件的 C# Windows 服务中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2140411/

相关文章:

c# - 系统.IO.IOException : Cannot close stream until all bytes are written

c# - Azure WebJob 和连接来自 Microsoft.Extensions.DependencyInjection 的 IServiceCollecton

c# - 如何通过一次单击在 GridView 下拉菜单中创建组合框?

python - 尝试在 python 中创建一个 virtualenv 并激活它

c - 即使在调用 fclose() 之后,由于 fopen() 而导致的内存泄漏

c# - 停止正常运行窗口的程序将关闭,并在有解决方案时通知您

c# - 在桌面背景上绘制作为墙纸替换 (Windows/C#)

c++ - WriteFile 到并行端口挂起

angularjs - Angular + Mocha 内存泄漏

memory-leaks - Sitecore实例崩溃-分析内存转储的最佳工具是什么?