Ping ping = new Ping();
ping.SendAsync(...);
(ping as IDisposable).Dispose();
Утечка памяти при использовании класса Ping (.NET 2.0, SP1)
Конечно, для повышения скорости работы решил пользоваться асинхронной посылкой эхозапроса SendAsync. Функция работает замечательно, адреса пингуются, но вот незадача. Она не освобождает ресурсы, а именно, для каждого экземпляра класса Ping создаются два хендла ОС (видимо под сокеты) и утекает несколько килобайт памяти. Все экземпляры Ping освобождаются через метод Dispose (трассировка это подтверждает) - с моей стороны все корректно. Утечку прекрасно видно диспетчере задач: создаются до полутора тысяч дескрипторов (для каждой волны пингования) и теряется до 40МБ памяти.
Как показали эксперименты, синхронная версия отправки эхозапроса Send не подвержена такому жуткому багу. Пришлось реализовывать множественное пингование через нее.
Такие вот дела.
UPD: .NET 2.0 SP2
Код:
Цитата: Der Meister
Бага? Если использовать метод, явно реализующий IDisposable (а у него такой имеется), то освобождает правильно.
Код:
Ping ping = new Ping();
ping.SendAsync(...);
(ping as IDisposable).Dispose();
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();
}
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();
}
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();
}
Забавно: завершаю проект, гоняя его под MS FxCop в поисках недочётов. Один из наиболее частых у меня - неверная реализация паттерна Dispose при наследовании. Понял минуты за три, где собака порыться могла :) И точно: та же самая утилита заостряет внимание на неверной работе функции освобождения ресурсов для Ping (там, кстати, ещё и GC.SupressFinalize() не вызывается, что, в общем случае, нехорошо).
Цитата: Der Meister
Они его тестировали, наверное, на using (Ping ping = ...)
Я поколупал класс Ping дизассемблером. Кароче картина маслом. Автор класса не переопределяет Dispose(bool) метод предка (класса Component), а внедряет новый Dispose() (для интерфейса IDisposable)! Тем самым формируя еще одну реализацию интерфейса IDisposable. Если я пишу ping.Dispose, естественно, вызывается виртуальный метод, который ясен фиг ничего не делает. :mad:
Явное приведение к типу IDisposable заставляет CLR вызывать метод Dispose у последней реализации этого интерфейса. Он-то какраз выполняет освобождение ресурсов.
Да, FxCop'ом стоило воспользоваться.
[QUOTE=hardcase]Автор класса не переопределяет Dispose(bool) метод предка (класса Component), а внедряет новый Dispose() (для интерфейса IDisposable)![/QUOTE]Странно это... То ли программист русский и был пьян, то ли они так "спрятали" багу посерьёзней.
Прошло несколько лет , а баг вроде как неисправлен? А я репу чесал с этим ping пока сюда не попал..