C#File System Watcher Windows服务发生了内存泄漏,无法跟踪导致内存泄漏的原因

问题描述

在过去的几天里,我一直在监视创建的Windows服务,因为我确定它存在内存泄漏。事实证明,我是对的-在过去几天中,它的内存使用量已从41MB增加到75MB。

此服务的全部作用是监视文件目录,每次在其中创建文件时,它将文件上传到我们的内容管理系统(Microfocus Content Manager);其他一些其他任务也正在执行,例如,如果发生某些异常,则写入事件日志,并将有关上传状态的消息写入日志文件

我想尝试用来发现此泄漏的一个想法是使用诸如.NET CLR分析器as proposed in answer to this question之类的东西。但是,探查器似乎不适用于Windows服务(因此,我将需要以某种方式将自己制作的服务更改为控制台应用程序吗?),而且我不确定它是否能够在运行时对应用程序进行探查? / p>

无论如何,这是Windows服务代码的完整副本。感谢所有能够阅读此内容并查看我是否做过愚蠢的事情导致泄漏的人,在此之下,我将谈论我认为可能导致内存泄漏的区域。

using HP.HPTRIM.SDK;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Configuration;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace BailiffReturnsuploader
{
    public partial class BailiffReturnsService : ServiceBase
    {
        private string FileWatchPath = ConfigurationManager.AppSettings["FileWatchPath"];
        private string UploadLogPath = ConfigurationManager.AppSettings["UploadLogPath"];
        private string UploadErrorLocation = ConfigurationManager.AppSettings["UploadErrorLocation"];
        private string ContentManagerEnviro = ConfigurationManager.AppSettings["ContentManagerEnviro"];

        public BailiffReturnsService()
        {
            InitializeComponent();
            bailiffEventLogger = new EventLog();
            if(!EventLog.sourceExists("BailiffReturnsSource"))
            {
                EventLog.CreateEventSource("BailiffReturnsSource","BailiffReturnsLog");
            }
            bailiffEventLogger.source = "BailiffReturnsSource";
            bailiffEventLogger.Log = "BailiffReturnsLog";
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                TrimApplication.Initialize();

                BailiffReturnsFileWatcher = new FileSystemWatcher(FileWatchPath)
                {
                    NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.Attributes,Filter = "*.*"
                };
                // I think this is the problematic part,the event registration?
                BailiffReturnsFileWatcher.Created += new FileSystemEventHandler(OnCreate);
                BailiffReturnsFileWatcher.EnableRaisingEvents = true;

                bailiffEventLogger.WriteEntry("Service has started",EventLogEntryType.@R_3_4045@ion);
            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry(string.Format("Could not create file listener : {0}",ex.Message),EventLogEntryType.Error);
            }
        }

        protected override void OnStop()
        {
            bailiffEventLogger.WriteEntry("Service has stopped",EventLogEntryType.@R_3_4045@ion);
            dispose();
        }

        /// <summary>
        /// Handler for file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnCreate(object sender,FileSystemEventArgs e)
        {
            try
            {
                int attempts = 0;
                FileInfo fileInfo = new FileInfo(e.FullPath);

                while (IsFileLocked(fileInfo))
                {
                    attempts++;
                    CreateUploadLog(UploadLogPath,string.Format("Info : {0} is locked,trying again. Attempt #{1}.",fileInfo.Name,attempts));
                    if (attempts == 5)
                    {
                        CreateUploadLog(UploadLogPath,string.Format("Error : {0} is locked,Could not access file within 5 attempts.",fileInfo.Name));
                        bailiffEventLogger.WriteEntry(string.Format("Error : {0} is locked,fileInfo.Name),EventLogEntryType.Error);
                        break;
                    }
                    Thread.Sleep(1500);
                }

                bool isSaveSuccess = SavetoTrim(e.FullPath);
                if(isSaveSuccess)
                {
                    DeleteFile(e.FullPath);
                }
                else
                {
                    MoveFiletoError(e.FullPath);
                }

                fileInfo = null;
                dispose();
            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry(string.Format("Error while saving or deleting file : {0}",EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Attemps to upload file to content manager.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        bool SavetoTrim(string path)
        {
            string pathFileNoExt = Path.GetFileNameWithoutExtension(path);

            try
            {
                string[] pathArgs = pathFileNoExt.Split(new string[] { "_" },StringSplitOptions.RemoveEmptyEntries);
                // Note for stack overflow: These classes and methods are part of an SDK provided by a 3rd party that I'm using to upload documents 
                // into their content management system. I'm not sure,but I don't think the memory leak is occuring at this part.
                
                using (Database dbCntMgr = new Database { Id = ContentManagerEnviro })
                {
                    
                    // Connect to the content manager database.
                    try
                    {
                        dbCntMgr.Connect();
                    }
                    catch (Exception ex)
                    {
                        bailiffEventLogger.WriteEntry(ex.Message,EventLogEntryType.Error);
                        CreateUploadLog(UploadLogPath,"Failed to connect to content manager.");
                        return false;
                    }

                    // Create the record based on record type,set default assignee and default bailiff type.
                    recordtype orecordtype = new recordtype(dbCntMgr,"Revenues - Bailiff");
                    Record oRecord = new Record(dbCntMgr,orecordtype);
                    oRecord.Assignee = new Location(dbCntMgr,"Bailiff Returns Pending");
                    oRecord.SetFieldValue(new FieldDeFinition(dbCntMgr,"Bailiff Type"),new UserFieldValue("Liability Order Return"));

                    // Set the default container,not changed if no result is found.
                    Record oContainer;
                    oContainer = new Record(dbCntMgr,"014/1065/0973/55198");

                    // Search via property ID and "80" for Revenues Property File
                    TrimMainObjectSearch search = new TrimMainObjectSearch(dbCntMgr,BaSEObjectTypes.Record);
                    TrimsearchClause trimsearchClause = new TrimsearchClause(dbCntMgr,BaSEObjectTypes.Record,new FieldDeFinition(dbCntMgr,"Property ID"));
                    trimsearchClause.SetCriteriaFromString(pathArgs[2].Substring(2));
                    search.AddSearchClause(trimsearchClause);
                    trimsearchClause = new TrimsearchClause(dbCntMgr,SearchClauseIds.recordtype);
                    trimsearchClause.SetCriteriaFromString("80");
                    search.AddSearchClause(trimsearchClause);

                    // Sets the container to found record if any are found.
                    foreach (Record record in search)
                    {
                        //oContainer = new Record(dbCntMgr,record.Uri);
                        oContainer = record;
                    }

                    // Once container is finalised,set record container to located container.
                    oRecord.Container = oContainer;

                    // Set the title to name
                    oRecord.Title = pathArgs[3];

                    // Set the input document.
                    InputDocument oInputDocument = new InputDocument();
                    oInputDocument.SetAsFile(path);
                    oRecord.SetDocument(oInputDocument,false,"Created Via Bailiff Content Manager Uploader service.");

                    // Save if valid,print error if not.
                    if (oRecord.Verify(false))
                    {
                        oRecord.Save();
                        CreateUploadLog(UploadLogPath,string.Format("File uploaded : {0}",Path.GetFileNameWithoutExtension(path)));
                        return true;
                    }
                    else
                    {
                        CreateUploadLog(UploadLogPath,string.Format("Upload of {0} file attempt did not meet validation criteria. Not uploaded.",Path.GetFileNameWithoutExtension(path)));
                        return false;
                    }
                }
            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry(ex.Message,EventLogEntryType.Error);
                return false;
            }
        }

        /// <summary>
        /// Deletes file when successfully uploaded.
        /// </summary>
        /// <param name="path"></param>
        void DeleteFile(string path)
        {
            try
            {
                string pathFileNoExt = Path.GetFileNameWithoutExtension(path);

                // If file exists,delete.
                if (File.Exists(path))
                {
                    File.Delete(path);
                    CreateUploadLog(UploadLogPath,string.Format("File deleted from Upload folder : {0}",pathFileNoExt));
                }
                else
                {
                    CreateUploadLog(UploadLogPath,string.Format("Error deleting file from upload folder : {0}",pathFileNoExt));
                }
            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry(ex.Message,EventLogEntryType.Warning);
                CreateUploadLog(UploadLogPath,ex.Message);
            }
        }

        /// <summary>
        /// Moves non uploaded files (Failed to upload) to an error location as specified in the app config.
        /// </summary>
        /// <param name="path"></param>
        void MoveFiletoError(string path)
        {
            try
            {
                string pathFileNoExt = Path.GetFileName(path);

                // If directory and file exist,attempt move.
                if(Directory.Exists(UploadErrorLocation))
                {
                    if(File.Exists(path))
                    {
                        if(File.Exists(Path.Combine(UploadErrorLocation,pathFileNoExt)))
                        {
                            File.Delete(Path.Combine(UploadErrorLocation,pathFileNoExt));
                        }

                        File.Move(path,Path.Combine(UploadErrorLocation,pathFileNoExt));
                    } else
                    {
                        CreateUploadLog(UploadLogPath,"Could not move non-uploaded file to error location");
                    }
                } else
                {
                    CreateUploadLog(UploadLogPath,"Could not move non-uploaded file to error location,does the error folder exist?");
                }

            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry("Error while moving file to error location : " + ex.Message,ex.Message);
            }
        }

        /// <summary>
        /// Takes full path of upload log path and a message to add to the upload log. Upload log location is specified in the app config.
        /// </summary>
        /// <param name="fullPath"></param>
        /// <param name="message"></param>
        private void CreateUploadLog(string fullPath,string message)
        {
            try
            {
                //StreamWriter streamWriter;

                // If file does not exist,create.
                if (!File.Exists(Path.Combine(fullPath,"UploadLog_" + DateTime.Now.ToString("ddMMyyyy") + ".txt")))
                {
                    using (StreamWriter streamWriter = File.CreateText(Path.Combine(fullPath,"UploadLog_" + DateTime.Now.ToString("ddMMyyyy") + ".txt")))
                    {
                        streamWriter.Close();
                    }
                }
                // Append text to file.
                using (StreamWriter streamWriter = File.AppendText(Path.Combine(fullPath,"UploadLog_" + DateTime.Now.ToString("ddMMyyyy") + ".txt")))
                {
                    streamWriter.WriteLine(string.Format("{0} -- {1}",DateTime.Now.ToString(),message));
                    streamWriter.Close();
                }
            }
            catch (Exception ex)
            {
                bailiffEventLogger.WriteEntry(ex.Message,EventLogEntryType.Warning);
            }
        }

        /// <summary>
        /// Attempts to access the file,returns true if file is locked,false if file is not locked.
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        private bool IsFileLocked(FileInfo file)
        {
            try
            {
                using(FileStream stream = file.Open(FileMode.Open,FileAccess.Read,FileShare.None))
                {
                    stream.Close();
                }
            }
            catch(IOException)
            {
                return true;
            }

            return false;
        }
    }
}

所以,我认为可能有一些区域可能导致泄漏:

  1. 事件注册/筹集-通过几次搜索一个或两个小时的试验,我怀疑是事件筹集和委派导致了内存泄漏。与事件完成后不注销或处置事件有关吗?
  2. 正在执行交互/上载的第三方SDK包含泄漏-这可能是可能的,如果答复认为这是原因,那么我将与SDK的维护者一起探讨此问题。我对此很有信心,这不是造成泄漏的原因,因为SDK包含调试模式,可以通过将任何未处理的对象输出到事件日志的方法来启用它,尝试过,它不会显示任何对象没有被处置。
  3. 写上载日志文件-可能会导致写有上载事件等文件的过程吗?我对这是内存泄漏没有太多信心,因为using语句已用于流写入器,但是也许吗?

您有什么想法?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)