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

如何解决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.Information);
            }
            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.Information);
            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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-