Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Утечка памяти при использовании класса Ping (.NET 2.0, SP1)

5
30 октября 2008 года
hardcase
4.5K / / 09.08.2005
Недавно столкнулся с задачей: пропинговать много, очень много адресов в локальной сети. Для ее решения воспользовался классом System.Net.NetworkInformation.Ping.
Конечно, для повышения скорости работы решил пользоваться асинхронной посылкой эхозапроса SendAsync. Функция работает замечательно, адреса пингуются, но вот незадача. Она не освобождает ресурсы, а именно, для каждого экземпляра класса Ping создаются два хендла ОС (видимо под сокеты) и утекает несколько килобайт памяти. Все экземпляры Ping освобождаются через метод Dispose (трассировка это подтверждает) - с моей стороны все корректно. Утечку прекрасно видно диспетчере задач: создаются до полутора тысяч дескрипторов (для каждой волны пингования) и теряется до 40МБ памяти.

Как показали эксперименты, синхронная версия отправки эхозапроса Send не подвержена такому жуткому багу. Пришлось реализовывать множественное пингование через нее.

Такие вот дела.

UPD: .NET 2.0 SP2
341
30 октября 2008 года
Der Meister
874 / / 21.12.2007
Бага? Если использовать метод, явно реализующий IDisposable (а у него такой имеется), то освобождает правильно.
 
Код:
Ping ping = new Ping();
ping.SendAsync(...);
(ping as IDisposable).Dispose();
5
30 октября 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Der Meister
Бага? Если использовать метод, явно реализующий IDisposable (а у него такой имеется), то освобождает правильно.
 
Код:
Ping ping = new Ping();
ping.SendAsync(...);
(ping as IDisposable).Dispose();

Действительно.
код

Код:
IPAddress[] ips = new IPAddress[] {
    new IPAddress(new byte[] { 10, 89, 3, 16 }),
    new IPAddress(new byte[] { 10, 89, 3, 18 }),
    new IPAddress(new byte[] { 10, 89, 3, 1 })
};

byte[] buffer = Encoding.ASCII.GetBytes("Ping string");

while (true) {
    foreach (IPAddress ip in ips) {
        Ping ping = new Ping();
        ping.PingCompleted += delegate(object sender, PingCompletedEventArgs e) {
            Console.WriteLine("Ping completed for {0}: {1}", e.Reply.Address, e.Reply.Status);
            ping.Dispose();
        };
        Console.WriteLine("Ping started for {0}", ip);
        ping.SendAsync(ip, 200, buffer, null);
    }
    Console.ReadKey();
}
Приводит к утечкам, тогда как
код
Код:
IPAddress[] ips = new IPAddress[] {
    new IPAddress(new byte[] { 10, 89, 3, 16 }),
    new IPAddress(new byte[] { 10, 89, 3, 18 }),
    new IPAddress(new byte[] { 10, 89, 3, 1 })
};

byte[] buffer = Encoding.ASCII.GetBytes("Ping string");

while (true) {
    foreach (IPAddress ip in ips) {
        Ping ping = new Ping();
        ping.PingCompleted += delegate(object sender, PingCompletedEventArgs e) {
            Console.WriteLine("Ping completed for {0}: {1}", e.Reply.Address, e.Reply.Status);
            [COLOR=Red](ping as IDisposable)[/COLOR].Dispose();
        };
        Console.WriteLine("Ping started for {0}", ip);
        ping.SendAsync(ip, 200, buffer, null);
    }
    Console.ReadKey();
}
Нет! Видимо объявление класса Ping некорректно и метод Dispose и метод, привязанный к интерфейсу IDisposable совсем разные вещи (кому-то руки оторвать нужно).
341
30 октября 2008 года
Der Meister
874 / / 21.12.2007
Они его тестировали, наверное, на using (Ping ping = ...)
Забавно: завершаю проект, гоняя его под MS FxCop в поисках недочётов. Один из наиболее частых у меня - неверная реализация паттерна Dispose при наследовании. Понял минуты за три, где собака порыться могла :) И точно: та же самая утилита заостряет внимание на неверной работе функции освобождения ресурсов для Ping (там, кстати, ещё и GC.SupressFinalize() не вызывается, что, в общем случае, нехорошо).
5
30 октября 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Der Meister
Они его тестировали, наверное, на using (Ping ping = ...)

Я поколупал класс Ping дизассемблером. Кароче картина маслом. Автор класса не переопределяет Dispose(bool) метод предка (класса Component), а внедряет новый Dispose() (для интерфейса IDisposable)! Тем самым формируя еще одну реализацию интерфейса IDisposable. Если я пишу ping.Dispose, естественно, вызывается виртуальный метод, который ясен фиг ничего не делает. :mad:
Явное приведение к типу IDisposable заставляет CLR вызывать метод Dispose у последней реализации этого интерфейса. Он-то какраз выполняет освобождение ресурсов.

Да, FxCop'ом стоило воспользоваться.

341
30 октября 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=hardcase]Автор класса не переопределяет Dispose(bool) метод предка (класса Component), а внедряет новый Dispose() (для интерфейса IDisposable)![/QUOTE]Странно это... То ли программист русский и был пьян, то ли они так "спрятали" багу посерьёзней.
93K
29 апреля 2014 года
Vovan88
1 / / 29.04.2014
Прошло несколько лет , а баг вроде как неисправлен? А я репу чесал с этим ping пока сюда не попал..
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог