XamDev
XamDev

Reputation: 3647

Memory leak issue in windows services in .net core

I am running the .net core application as windows service. However, after deployment on production I am facing memory leak issue, memory usage is continuously getting increased.

Below is my windows service,

public static class SendMailHostServiceExtensions
    {
      public static void RunAsSendMailService(this IWebHost host)
        {
            var webHostService = new SendMailHostService(host);
            webHostService.ServiceName = "LMS.WinService.SendEmail";
            ServiceBase.Run(webHostService);
        }
    }

and this is the class which is actually executing the service logic

public class SendMailHostService : WebHostService
{
    public SendMailHostService(IWebHost host) : base(host)
        {

        }

    protected override void OnStarted()
        {
            timer = new System.Timers.Timer(TimerInterval);
            timer.Elapsed += OnElapsedTime;
            timer.Enabled = true;

       }

    private void OnElapsedTime(object source, ElapsedEventArgs e)
       {
          try
            {
                StartSendMail();
            }
            catch (Exception ex)
            {
                Log(ex.ToString());
            }
       }
    private void StartSendMail()
    {
        string connectionString = string.Empty;
        bool isSentSuccess = false;
        int errorCount = 0;
        var context = new ApplicationDbContext();
        try
        {
            Hashtable htFilePaths = new Hashtable();

            {
                List<LMS_Client> clientList = context.Clients.ToList();

                if (clientList.Count > 0)
                {
                    foreach (var item in clientList)
                    {
                        connectionString = string.Empty;
                        connectionString = "Server=" + item.ServerName + ";Database=" + item.DatabaseName + ";uid=" + item.UserName + ";password=" + item.Password;
                        strclientName = item.ClientName;

                        context = new ApplicationDbContext(connectionString);
                        List<LMS_MessageSend> result = context.MessageSend.Where(x => x.IsSent == false && x.ErrorCount < CheckErrorCount).ToList();

                        if (result.Count > 0)
                        {
                            foreach (var data in result)
                            {

                                strToName = data.ToName == null ? data.ToEmail : data.ToName;
                                strToAddress = data.ToEmail;
                                strFromNameAddressString = data.FromNameAddress;
                                strBody = data.BodyText;
                                strSubject = data.Subject;
                                strCC = data.CCEmail;
                                strBCC = data.BCCEmail;

                                if (CheckAttachment)
                                {
                                    List<LMS_MessageSendAttachment> attachments = context.MessageSendAttachment.Where(x => x.MessageSendID == data.MessageSendID).ToList();

                                    if (attachments.Count > 0)
                                    {
                                        foreach (var attachment in attachments)
                                        {
                                            strAttachmentPath = attachment.AttachmentPath == null ? string.Empty : attachment.AttachmentPath;

                                            if (strAttachmentPath != string.Empty)
                                            {
                                                string[] fileArr = strAttachmentPath.Split(Convert.ToChar("|"));

                                                if (fileArr.LongLength == 2)
                                                {
                                                    htFilePaths.Add(MailAttachmentDrive + fileArr[0], MailAttachmentLocalDrive + fileArr[1]);
                                                }
                                            }
                                        }
                                    }
                                }

                                isSentSuccess = SendEmail(strToName, strToAddress, strFromNameAddressString, strSubject, strBody, htFilePaths, strCC, strBCC);


                                data.IsSent = isSentSuccess;
                                data.ErrorCount = isSentSuccess == false ? data.ErrorCount + 1 : 0;
                                data.SentDate = DateTime.Now;

                            }

                            context.UpdateRange(result);
                            context.SaveChanges();

                        }
                    }
                }



            }
        }
        catch (Exception ex)
        {
            isSentSuccess = false;
            errorCount = 1;

            Log(ex.ToString());
        }

    }

private bool SendEmail(string ToName, string ToEmail, string FromMail, string Subject, string BodyText, Hashtable htFilePaths, string CC, string BCC)
    {
        string[] addrArray = new string[0];
        bool isSentSuccess = false;
        IDictionaryEnumerator icKeys;
        SmtpClient client = new SmtpClient();

        try
        {
            using (MailMessage mm = new MailMessage())
            {

                ToEmail = ToEmail.Substring(0, 1) == ";" ? ToEmail.Substring(1, ToEmail.Length - 1) : ToEmail;
                ToEmail = ToEmail.Substring(ToEmail.Length - 1, 1) == ";" ? ToEmail.Substring(0, ToEmail.Length - 1) : ToEmail;
                addrArray = ToEmail.Split(';');

                if (UseGoogleAuthentication)
                {
                    mm.To.Add(new MailAddress("gmail address"));
                }
                else
                {
                    foreach (string emailAddr in addrArray)
                    {
                        mm.To.Add(new MailAddress(emailAddr));
                    }
                }

                if (!string.IsNullOrEmpty(CC))
                {
                    CC = CC.Substring(0, 1) == ";" ? CC.Substring(1, CC.Length - 1) : CC;
                    CC = CC.Substring(CC.Length - 1, 1) == ";" ? CC.Substring(0, CC.Length - 1) : CC;
                    addrArray = CC.Split(';');
                    foreach (string emailAddr in addrArray)
                    {
                        mm.CC.Add(new MailAddress(emailAddr.Trim()));
                    }
                }

                if (!string.IsNullOrEmpty(BCC))
                {
                    BCC = BCC.Substring(0, 1) == ";" ? BCC.Substring(1, BCC.Length - 1) : BCC;
                    BCC = BCC.Substring(BCC.Length - 1, 1) == ";" ? BCC.Substring(0, BCC.Length - 1) : BCC;
                    addrArray = BCC.Split(';');
                    foreach (string emailAddr in addrArray)
                    {
                        mm.Bcc.Add(new MailAddress(emailAddr.Trim()));
                    }
                }

                mm.From = UseGoogleAuthentication == false ? mm.From = new MailAddress(FromMail) : new MailAddress("some gmail address");

                mm.Subject = Subject;
                mm.Body = BodyText;
                mm.BodyEncoding = Encoding.UTF8;
                mm.IsBodyHtml = true;

                if (CheckAttachment)
                {
                    if (htFilePaths != null && htFilePaths.Count > 0)
                    {
                        icKeys = htFilePaths.GetEnumerator();
                        while (icKeys.MoveNext())
                        {
                            if (icKeys.Key.ToString() != string.Empty && icKeys.Value.ToString() != string.Empty)
                            {
                                if (string.Compare(icKeys.Key.ToString(), icKeys.Value.ToString(), true) != 0)
                                {
                                    File.Copy(icKeys.Key.ToString(), icKeys.Value.ToString(), true);
                                }

                                Attachment ma = new Attachment(icKeys.Value.ToString());
                                mm.Attachments.Add(ma);

                            }
                        }
                    }
                }


                if (UseGoogleAuthentication)
                {
                    client.UseDefaultCredentials = false;
                    client.Host = "smtp.gmail.com";
                    client.Port = 587;
                    client.EnableSsl = true;
                    client.DeliveryMethod = SmtpDeliveryMethod.Network;
                    client.Credentials = new NetworkCredential("some gmail address", "gmail password");
                    client.Timeout = 20000;
                }
                else
                {
                    client.Host = SMTPServer;
                    client.UseDefaultCredentials = false;
                }

                client.Send(mm);
                isSentSuccess = true;


            }
        }

        catch (Exception ex)
        {
            Log(ex.ToString());
        }
        finally
        {
            if (client != null) client.Dispose();
        }
        if (htFilePaths != null && htFilePaths.Count > 0)
        {
            icKeys = htFilePaths.GetEnumerator();

            while (icKeys.MoveNext())
            {
                File.Delete(icKeys.Value.ToString());
            }
        }

        return isSentSuccess;
    }

}

After disposing the objects created still the memory usage is getting increased.

Any help on this appreciated !

Upvotes: 1

Views: 4965

Answers (1)

Cee McSharpface
Cee McSharpface

Reputation: 8726

Identify all disposable managed objects that you instantiate (new keyword) or obtain from a factory (disposable = object implements the IDisposable interface). Then wrap them in a using block, like this:

using(var ctx = new ApplicationDbContext(connectionstring)
{
    /* once ctx goes out of scope, it will automatically be disposed */
}

In the code you posted, ApplicationDbContext and SmtpClient are candidates for this. Whenever you assign another new instance to a variable that already holds a reference, that previous reference is leaked. So every reference needs to be disposed:

foreach(var hi in hashtables_can_be_foreached_too)
{
    using(var o = new SomeDisposableObject())
    {
        /* code that works on o */
    }
}

When dealing with memory leaks, you always need to be aware of some complexity:

  • Disposing and finalizing objects does not mean that heap storage is reclaimed immediately. .NET uses multi generation garbage collection.
  • Memory allocation shown in windows task manager is not accurate. Use profiling tools available for the .NET framework

Upvotes: 2

Related Questions