繁体   English   中英

为什么此代码(可能是事件处理程序)在我的 c# windows 服务中导致 memory 泄漏?

[英]Why is this code (possibly event handler) causing a memory leak in my c# windows service?

在 windows 服务中,我知道我有 memory 泄漏。 我怎么知道在这个问题的 scope 之外,但你可以在这里看到最初的问题

我有一些类的 windows 服务:

public partial class VLSService : ServiceBase
{

   ReportedContentProcess reportedContent;

   protected override void OnStart(string[] args)
   {
        //when a user reports a video
        reportedContent = new ReportedContentProcess();
        reportedContent.ProcessTimer.Elapsed += new ElapsedEventHandler(ReportedContentTimer_Elapsed);

   }

   void ReportedContentTimer_Elapsed(object sender, ElapsedEventArgs e)
   {
        reportedContent = new ReportedContentProcess();
        reportedContent.Process();
        reportedContent.ProcessReinstated();
   }

}


public class ReportedContentProcess : ProcessBase
    {

        //location on the library
        string libraryArchivedFilePath;
        string libraryRealHiFilePath;
        string libraryRealLoFilePath;
        string libraryFlashHiFilePath;
        string libraryFlashLoFilePath;

        //location on the reported folder
        string reportedContentArchivedFilePath;
        string reportedContentRealHiFilePath;
        string reportedContentRealLoFilePath;
        string reportedContentFlashHiFilePath;
        string reportedContentFlashLoFilePath;

        string reportedContentFolderPath;
        static EmailSettings emailSettings;

        /// <summary>
        /// This process will move reported content out of the 'real' and 'flash' mounted folders on the 
        /// hard drive/ storeage location so they cannot under any circumstances be got to by any users
        /// of the library.
        /// </summary>
        public ReportedContentProcess(): base(1021)
        {
            DirectoryInfo arciveFolderPathInfo = new DirectoryInfo(fileSystemReferencesForService.ArchiveDir);
            DirectoryInfo contentFolder = arciveFolderPathInfo.Parent;
            reportedContentFolderPath = contentFolder.FullName.ToString() + @"\ReportedContent\";
            emailSettings = settingsManagerForService.GetEmailSettings();
        }

        public override void Process()
        {

            if (!EnumsAndConstants.ApplicationLocks.ReportedContentProcessRunning)
            {

                EnumsAndConstants.ApplicationLocks.ReportedContentProcessRunning = true;

                videosToProcess = contentManagerForService.GetReportedVideos(false);

                //get the reportedvideo object for this video
                CreateReportedVideoContentFolder();

                ReportedVideo reportedVideo;

                foreach (Video v in videosToProcess)
                {

                    string flashVideoExt = string.Empty;

                    if (v.IsAudio)
                    {
                        flashVideoExt = ".mp3";
                    }
                    else
                    {
                        flashVideoExt = ".mp4";
                    }

                    //library location of each file for video
                    libraryArchivedFilePath = fileSystemReferencesForService.ArchiveDir + v.LocalFile;

                    libraryRealHiFilePath = fileSystemReferencesForService.RealDir + v.Url.ToString() + "_hi.rm";
                    libraryRealLoFilePath = fileSystemReferencesForService.RealDir + v.Url.ToString() + "_lo.rm";
                    libraryFlashHiFilePath = fileSystemReferencesForService.FlashDir + v.Url.ToString() + "_hi" + flashVideoExt;
                    libraryFlashLoFilePath = fileSystemReferencesForService.FlashDir + v.Url.ToString() + "_lo" + flashVideoExt;

                    //new location for file to go to
                    reportedContentArchivedFilePath = reportedContentFolderPath + v.LocalFile;


}

}


/// <summary>
/// A base class that holds all the Global objects for any process that operates under the 
/// service. This process works with 
/// </summary>
public abstract class ProcessBase
{
    public Timer processTimer;
    public Timer ProcessTimer{get{ return processTimer;}set{processTimer=value;}}

    protected SqlConnection connection;
    protected VlsContent contentManagerForService;
    protected VlsSecurity securityManagerForService;
    protected VlsSettings settingsManagerForService;
    protected FileSystemReferences fileSystemReferencesForService;
    protected List<Video> videosToProcess;
    protected ExeReferences exeReferenecesForService;
    protected GeneralSettings generalSettingsForService;

    public abstract void Process();


//sets up all the common objects
public ProcessBase() 
{

    connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Db"].ToString());
    contentManagerForService = new VlsContent(connection);
    settingsManagerForService = new VlsSettings(connection);
    securityManagerForService = new VlsSecurity(connection);
    fileSystemReferencesForService = settingsManagerForService.GetFileSystemReferences();
    exeReferenecesForService = settingsManagerForService.GetExeReferences();
    generalSettingsForService = settingsManagerForService.GetGeneralSettings();

}


//This constructor will call the default constructor ^
protected ProcessBase(long intervalArg) : this()
{
    processTimer = new Timer(intervalArg);
    processTimer.Enabled = true;

}

}

在分析此代码后,这似乎导致了 memory 泄漏。 我想知道为什么?

我认为有问题的行是:

reportedContent = new ReportedContentProcess(); [located in the event handler]

但我真的不明白为什么。 当然,它会在 memory 中创建称为“reportedContent”的指针,然后当调用上述内容时,它将在堆上放置实际值,并为 ReportedContentProcess() 的成员提供新值。 然后,当事件处理程序在大约 1 秒后再次运行时,它会将 GC 根指针“reportedContent”替换为 ReportedContentProcess() class 的新分配堆项。 然后旧的(以及所有现在废弃的子对象将被垃圾收集,因为调用堆栈不再引用它们的根......?这应该一遍又一遍地发生(旧的和新的)风格.

希望有些人可以帮助我有点希望这是问题,所以我可以解决它,但想在开始重构代码之前检查。

简介在这里:

在此处输入图像描述

我不知道ProcessBase是什么,但我认为这是您的问题所在。

它是否处理 .NET/C# 环境之外的任何内容? 它是否应该实现 IDisposable 接口,并且是否应该在创建新的ReportedProcess之前释放现有的 ReportedProcess。

此外,您还为OnStart()中的初始ReportedProcess创建了一个事件处理程序。 然后,您通过为其引用分配一个新实例来丢失对第一个事件的引用,但是初始ReportedProcess保持活动状态,因为它包含一个包含已订阅事件处理程序的 object,它不会被垃圾收集,直到程序结束,技术上是 memory 泄漏。

如果在ReportedProcess class 的某个地方,您在清理事件处理程序方面应用相同的策略,那么您的问题就存在。

**更新的答案**

只是查看您更新的代码,它缺少 ProcessReinstated() 代码,但我认为这可能是您没有明确关闭您的 SqlConnection 的事实。 您的 ProcessBase 应该实现一个 IDisposable 接口,并且您的计时器代码应该在实例化新类之前显式处理现有的 ReportedProcess 类,这是数据库连接和 sockets 和其他外部连接之类的最佳策略。

它似乎没有 memory 泄漏。 有没有类似这样的代码:

reportedContent.ProcessTimer.Elapsed += new ElapsedEventHandler(ReportedContentTimer_Elapsed);

如果你有,那么你有一个对你创建的所有报告的Contect 的引用,并且 GC 不会释放它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM