Почему этот код (возможно, обработчик событий) вызывает утечку памяти в моем c # windows service? - PullRequest
1 голос
/ 28 июня 2011

В службе Windows я знаю, что у меня утечка памяти.Насколько я знаю, это выходит за рамки этого вопроса, но вы можете увидеть начальный вопрос здесь

У меня есть служба 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;

}

}

После профилирования этого кода кажется, что это вызывает утечку памяти.Что мне интересно, почему?

Я думаю, что проблемная строка такова:

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

Но я не могу понять, почему.Конечно, он создаст указатель в памяти с именем «connectedContent», затем при вызове вышеизложенного фактическое значение будет помещено в кучу с новыми значениями для членов ReportedContentProcess ().Затем, когда обработчик событий снова запустится примерно через 1 секунду, он просто заменит корневой указатель GC «reportContent» новым выделенным элементом кучи для класса ReportedContentProcess ().Затем старый (и все его теперь заброшенные дочерние объекты будут собраны, так как на их корень больше не ссылается стек вызовов). Это должно происходить снова и снова (со старым и новым).

Надеюсь, что кто-то может помочь. Я надеюсь, что это проблема, поэтому я могу ее исправить, но хочу проверить, прежде чем начать перефакторинг кода.

Профиль здесь:

enter image description here

Ответы [ 2 ]

1 голос
/ 28 июня 2011

Я не знаю, что такое ProcessBase, но я думаю, что именно в этом ваша проблема.

Имеет ли это отношение к чему-либо за пределами среды .NET / C #?Если он реализует интерфейс IDisposable и существующий ReportedProcess должен быть удален до создания нового.

Кроме того, вы создаете обработчик события для исходного ReportedProcess в OnStart().Затем вы теряете ссылку на это с первым событием, назначая новый экземпляр этой ссылке, однако начальный ReportedProcess сохраняется благодаря тому факту, что у него есть содержащийся объект, у которого есть подписанный обработчик событий, он не будет мусоромСобран до конца программы и технически является утечкой памяти.

Если где-то в классе ReportedProcess вы применяете ту же стратегию в отношении очистки обработчиков событий, то есть ваша проблема.

** ОБНОВЛЕННЫЙ ОТВЕТ **

Просто глядя на ваш обновленный код, он пропускает код ProcessReinstated (), но я думаю, что это может быть тот факт, что вы явно не Закрыть вашего SqlConnection.Ваша ProcessBase должна реализовывать интерфейс IDisposable, а ваш код таймера должен явно удалять существующие классы ReportedProcess перед созданием новых, это лучшая стратегия с такими вещами, как соединения с базой данных, сокеты и другие внешние соединения.

0 голосов
/ 28 июня 2011

Кажется, что нет утечки памяти. Есть ли у вас код, похожий на этот:

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

Если у вас есть, то у вас есть ссылка на все созданные учетные данные, которые вы создали, и GC не выпускает их.

...