обработчики событий .net, использующие очистку filesystemwatcher - PullRequest
1 голос
/ 19 мая 2011

Я написал простой инструмент для тестирования с использованием класса .net FileSystemWatcher. Проблема в том, что я получил утечку памяти, потому что на экземпляры MemoryLeakTest ссылается измененный обработчик в FileSystemWatcher. Как правильно очистить эту ссылку, чтобы сборщик мусора мог впоследствии собрать экземпляр MemoryLeakTest и экземпляр FileSystemWatcher?

Чтобы увидеть пропущенные экземпляры в куче, следуйте этим инструкциям: http://blogs.msdn.com/b/calvin_hsia/archive/2008/04/11/8381838.aspx

Заранее спасибо за совет

using System;
using System.IO;

namespace MemoryLeakTest {

class Leaking {

    private FileSystemEventHandler changedHandler;
    private FileSystemWatcher fsw;

    public Leaking() {
        changedHandler = new FileSystemEventHandler(fsw_Changed);

        fsw = new FileSystemWatcher("c:\\", "*.*");
        fsw.Changed += changedHandler;
        fsw.EnableRaisingEvents = true;
    }

    ~Leaking() {
        fsw.Changed -= changedHandler;
        fsw.Dispose();                        
    }

    void fsw_Changed(object sender, FileSystemEventArgs e) {
        Console.WriteLine("Changed");
    }

}


class Program {
    static void Main(string[] args) {

        for (int i = 0; i < 100; ++i) {
            var x = new Leaking();
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.ReadLine();

    }
}
}

Ответы [ 2 ]

2 голосов
/ 30 августа 2011

Возможно, вы столкнулись с утечкой памяти в самом FileWatcher. Смотрите следующее: http://connect.microsoft.com/VisualStudio/feedback/details/565720/memory-leak-in-system-io-filesystemwatcher

1 голос
/ 19 мая 2011

Да, вы должны реализовать IDisposable в каждом классе, в котором есть член, реализующий IDisposable. Смотри http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=VS.100).aspx.

Однако реализация IDisposable просто обеспечивает своевременную очистку ресурсов. Как правило, отказ от реализации IDisposable не приведет к длительной утечке памяти, поскольку сборщик мусора в конечном итоге сможет запустить финализаторы, которые освободят ресурсы операционной системы. Но отсутствие утилизации может привести к кратковременным утечкам памяти и истощению ресурсов. может вызывать долговременные проблемы, но это довольно редко встречается при работе с объектами BCL.

Например, в приведенной выше тестовой программе созданные вами объекты Leaking становятся «без корневыми» после выхода из цикла, и, как вы видели, сборщик мусора действительно собирает их. Если вы позволите вашей программе работать некоторое время, занимаясь другими делами, эти объекты будут собраны во время следующей сборки мусора. Это кратковременная утечка памяти и ресурсов, но не долгосрочная проблема.

Теперь вы можете столкнуться с утечкой памяти, если у вас что-то вроде:

FileSystemWatcher watcher = new FileSystemWatcher(...);
void MakeABigLeak()
{
    for (int i = 0; i < 10; ++i)
    {
        var MyObject = new SomeObject();
        watcher.Changed += MyObject.ChangeHandler;
    }
}

В этом случае watcher содержит ссылку на каждый из созданных объектов. И поскольку watcher является корнем, эти объекты останутся активными. Они не будут собирать мусор.

Единственный способ исправить это - убедиться, что объект удаляется из уведомления о событии. Вы можете сделать это в методе Dispose объекта, хотя есть и другие способы. Однако вы должны быть осторожны , а не , чтобы сделать это в финализаторе. Помните, что финализаторы работают без установленного порядка. Возможно, что watcher было завершено до объекта, который ссылается на него (поскольку ни один из них не является корневым, порядок завершения не имеет значения). Вот почему существует метод Dispose(bool): для предотвращения доступа к другим объектам во время финализации.

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

...