Утечка памяти
От: snaphold  
Дата: 20.02.15 10:18
Оценка:
Привет

читаю книгу и там приведено 2 примера с утечкой памяти.
Объясните пожалуйста

1

class Host
{
public event EventHandler Click;
}
class Client
{
Host _host;
public Client (Host host)
{
_host = host;
_host.Click += HostClicked;
}
void HostClicked (object sender, EventArgs e) { ... }
}

You might expect that after CreateClients finishes executing, the 1,000 Client
objects will become eligible for collection. Unfortunately, each client has another
referee: the _host object whose Click event now references each Client instance.
This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
doesn’t do anything to attract attention.


не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?

2

class Foo
{
Timer _timer;
Foo()
{
_timer = new System.Timers.Timer { Interval = 1000 };
_timer.Elapsed += tmr_Elapsed;
_timer.Start();
}
void tmr_Elapsed (object sender, ElapsedEventArgs e) { ... }
}

Unfortunately, instances of Foo can never be garbage-collected! The problem is
the .NET Framework itself holds references to active timers so that it can fire their
Elapsed events. Hence:
• The .NET Framework will keep _timer alive.
• _timer will keep the Foo instance alive, via the tmr_Elapsed event handler.


ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

Что я не понимаю?
Спасибо
Re: Утечка памяти
От: Sinix  
Дата: 20.02.15 10:56
Оценка:
Здравствуйте, snaphold, Вы писали:

S>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


Host хранит внутри себя список подписчиков. Пока этот список не очистить — подписчики будут жить. Приведённый код очистку не делает

S>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

Подвох: таймер хранит в себе ссылку на Foo, foo может держать ещё кучу объектов.

Правило большого пальца:
для IDisposable нужно или вызывать Dispose(), или знать что делаешь (исключения типа Task как раз из этого разряда), или быть готовым к утечкам типа такой.

А объяснение в книге корявое, да. Поискал по тексту — это ж c# от Аблахари, да? Листал очень давно, осталось впечатление, что воды много, толку мало.
Re: Утечка памяти
От: Mr.Delphist  
Дата: 20.02.15 10:57
Оценка:
Здравствуйте, snaphold, Вы писали:

S>Привет


S>читаю книгу и там приведено 2 примера с утечкой памяти.

S>Объясните пожалуйста

S>1


S>

S>class Host
S>{
S>public event EventHandler Click;
S>}
S>class Client
S>{
S>Host _host;
S>public Client (Host host)
S>{
S>_host = host;
S>_host.Click += HostClicked;
S>}
S>void HostClicked (object sender, EventArgs e) { ... }
S>}

S>You might expect that after CreateClients finishes executing, the 1,000 Client
S>objects will become eligible for collection. Unfortunately, each client has another
S>referee: the _host object whose Click event now references each Client instance.
S>This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
S>doesn’t do anything to attract attention.


S>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


Сколько живёт Host и сколько живёт Client? Ибо совершенно точно указано, что клиент не может быть собран, пока на него ссылается хост.
Если они умирают вместе, то, насколько помню, дотнетный GC умеет такое утилизировать (в отличие от COM GC). Если же один живёт, а другой уже всё — события надо отвязывать.
Re: Утечка памяти
От: Sharov Россия  
Дата: 20.02.15 10:58
Оценка:
Здравствуйте, snaphold, Вы писали:

S>Привет


S>читаю книгу и там приведено 2 примера с утечкой памяти.

S>Объясните пожалуйста

S>1


S>

S>class Host
S>{
S>public event EventHandler Click;
S>}
S>class Client
S>{
S>Host _host;
S>public Client (Host host)
S>{
S>_host = host;
S>_host.Click += HostClicked;
S>}
S>void HostClicked (object sender, EventArgs e) { ... }
S>}

S>You might expect that after CreateClients finishes executing, the 1,000 Client
S>objects will become eligible for collection. Unfortunately, each client has another
S>referee: the _host object whose Click event now references each Client instance.
S>This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
S>doesn’t do anything to attract attention.



Предполагается, что объект должен быть убран GC, но не может в виду ссылок на него со
стороны обработчиков событий. Т.е. необходимо отписываться от обработчиков для грамотного
сбора мусора.

S>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


S>2


S>

S>class Foo
S>{
S>Timer _timer;
S>Foo()
S>{
S>_timer = new System.Timers.Timer { Interval = 1000 };
S>_timer.Elapsed += tmr_Elapsed;
S>_timer.Start();
S>}
S>void tmr_Elapsed (object sender, ElapsedEventArgs e) { ... }
S>}

S>Unfortunately, instances of Foo can never be garbage-collected! The problem is
S>the .NET Framework itself holds references to active timers so that it can fire their
S>Elapsed events. Hence:
S>• The .NET Framework will keep _timer alive.
S>• _timer will keep the Foo instance alive, via the tmr_Elapsed event handler.


S>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.


Предполагается нарушение семантики -- возможно, что Вы не хотели, чтобы объект был вечно живущий...
а он оказался
Кодом людям нужно помогать!
Re: Утечка памяти
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.15 10:58
Оценка: -1
Здравствуйте, snaphold, Вы писали:

S>Привет


S>читаю книгу и там приведено 2 примера с утечкой памяти.

S>Объясните пожалуйста

S>1


S>You might expect that after CreateClients finishes executing, the 1,000 Client

S>objects will become eligible for collection. Unfortunately, each client has another
S>referee: the _host object whose Click event now references each Client instance.
S>This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
S>doesn’t do anything to attract attention.
S>[/q]

S>не понимаю откуда тут взятся утечке.

Вы не привели код CreateClients, про который говорится в тексте. Идея — в том, что Host удерживает ссылку на Client, и если Host жив, то клиенты тоже будут вынуждены оставаться живыми.
S>Что мешает объекту Client почистить после себя Host?
Что такое "после себя"? Что такое "почистить"? Отписаться от события? В какой момент? Т.е. куда бы вы добавили строчку _host.Click -= HostClicked?

S>2


S>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

А что это, по вашему, означает? Это и есть, по определению, утечка памяти.
Если вы сделаете цикл типа for(int i=0; i<1000000; i++) var f = new Foo(); - то всё, копец, система ляжет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Утечка памяти
От: Sinix  
Дата: 20.02.15 11:02
Оценка: :)))
Здравствуйте, Sinclair, Вы писали:
Здравствуйте, Sharov, Вы писали:
Здравствуйте, Mr.Delphist, Вы писали:
Здравствуйте, Sinix, Вы писали:

Триплет квадруплет. Что характерно, контрольные суммы сошлись

UPD + fmiracle. Горшочек_не_вари.
Отредактировано 20.02.2015 11:05 Sinix . Предыдущая версия . Еще …
Отредактировано 20.02.2015 11:04 Sinix . Предыдущая версия .
Re: Утечка памяти
От: fmiracle  
Дата: 20.02.15 11:03
Оценка:
Здравствуйте, snaphold, Вы писали:

S>читаю книгу и там приведено 2 примера с утечкой памяти.

S>Объясните пожалуйста

S>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


Утечки памяти в .NET это именно что забыто где была ссылка. Это может быть не то что совсем "утечка", но разницы мало. Ты думал, что память освободится, а она не освобождается. Потому что есть незаметная на первый взгляд живая ссылка на ставший уже ненужным объект через его событие.

S>2

S>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

Суть в том, что если ты создал new Foo, поработал и убрал на него все ссылки, то он должен удалиться из памяти (так обычно ведут себя объекты в .net, и так ожидает программист), но данный не удалится из-за активного таймера. Если в цикле будешь создавать и выбрасывать объекты Foo, то память быстро исчерпается.
Re[2]: Утечка памяти
От: snaphold  
Дата: 20.02.15 11:37
Оценка: -2
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, snaphold, Вы писали:


S>>Привет


S>>читаю книгу и там приведено 2 примера с утечкой памяти.

S>>Объясните пожалуйста

S>>1


S>>You might expect that after CreateClients finishes executing, the 1,000 Client

S>>objects will become eligible for collection. Unfortunately, each client has another
S>>referee: the _host object whose Click event now references each Client instance.
S>>This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
S>>doesn’t do anything to attract attention.
S>>[/q]

S>>не понимаю откуда тут взятся утечке.

S>Вы не привели код CreateClients, про который говорится в тексте. Идея — в том, что Host удерживает ссылку на Client, и если Host жив, то клиенты тоже будут вынуждены оставаться живыми.
S>>Что мешает объекту Client почистить после себя Host?
S>Что такое "после себя"? Что такое "почистить"? Отписаться от события? В какой момент? Т.е. куда бы вы добавили строчку _host.Click -= HostClicked?

в финализатор

S>>2


S>>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

S>А что это, по вашему, означает? Это и есть, по определению, утечка памяти.

утечка процесс неконтролируемого уменьшения памяти

S>Если вы сделаете цикл типа for(int i=0; i<1000000; i++) var f = new Foo(); - то всё, копец, система ляжет.


имеете ввиду, что после выполнения такого кода Foo станут недоступны, а память останется висеть из-за таймеров?

void main()
{
for(int i=0; i<1000000; i++) var f = new Foo();
}

Re[2]: Утечка памяти
От: snaphold  
Дата: 20.02.15 11:39
Оценка:
Здравствуйте, fmiracle, Вы писали:

F>Здравствуйте, snaphold, Вы писали:


S>>читаю книгу и там приведено 2 примера с утечкой памяти.

S>>Объясните пожалуйста

S>>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


F>Утечки памяти в .NET это именно что забыто где была ссылка. Это может быть не то что совсем "утечка", но разницы мало. Ты думал, что память освободится, а она не освобождается. Потому что есть незаметная на первый взгляд живая ссылка на ставший уже ненужным объект через его событие.


S>>2

S>>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.

F>Суть в том, что если ты создал new Foo, поработал и убрал на него все ссылки, то он должен удалиться из памяти (так обычно ведут себя объекты в .net, и так ожидает программист), но данный не удалится из-за активного таймера. Если в цикле будешь создавать и выбрасывать объекты Foo, то память быстро исчерпается.


а это только с таймерами и event такая подстава?
Re[3]: Финализатор
От: Qbit86 Кипр
Дата: 20.02.15 11:46
Оценка:
Здравствуйте, snaphold, Вы писали:

S>>Что такое "после себя"? Что такое "почистить"? Отписаться от события? В какой момент? Т.е. куда бы вы добавили строчку _host.Click -= HostClicked?


S>в финализатор


Финализатор не предназначен для этих целей.

Кроме того, у тебя нет гарантий относительно того, финализирован ли уже объект, на который ссылается _host.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Утечка памяти
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.15 11:47
Оценка: +1
Здравствуйте, snaphold, Вы писали:
S>>Что такое "после себя"? Что такое "почистить"? Отписаться от события? В какой момент? Т.е. куда бы вы добавили строчку _host.Click -= HostClicked?
S>в финализатор
финализатор не будет вызван до тех пор, пока Host удерживает ссылку на экземпляр Client .

S>утечка процесс неконтролируемого уменьшения памяти

Нет. Утечка — неосвобождение памяти. Просто в маленьких масштабах она незаметна. А в программах, работающих 24x7, даже маленькая утечка приводит к тому, что постепенно занятой памяти становится всё больше.

S>имеете ввиду, что после выполнения такого кода Foo станут недоступны, а память останется висеть из-за таймеров?

Ну почему же "после". Я бы сказал "в процессе".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Утечка памяти
От: Sinix  
Дата: 20.02.15 11:54
Оценка: +1
Здравствуйте, snaphold, Вы писали:

S>а это только с таймерами и event такая подстава?


В теории, с любым объектом, являющимся GC root: static-переменная, переменная в TLS/Call context, переменная на стеке, объекты в freachable/fqueue, exposed com и т.д. и т.п. Ну и со всем доступным через gc roots: переменные, поля, списки-массивы, делегаты-события, замыкания, итераторы, авайтеры...

Другое дело, что большинство утечек сводится к:
* не вызвал Dispose()
* долгоживущий объект захавал ссылку и не отдаёт.

Найти такие места не так уж и сложно, при наличии memory profiler — вообще элементарно.
Re[3]: Утечка памяти
От: fmiracle  
Дата: 20.02.15 12:23
Оценка:
Здравствуйте, snaphold, Вы писали:

S>а это только с таймерами и event такая подстава?


Это, наверное, самые хитрые, которые легко пропустить.
Re[4]: Финализатор
От: snaphold  
Дата: 20.02.15 12:39
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, snaphold, Вы писали:


S>>>Что такое "после себя"? Что такое "почистить"? Отписаться от события? В какой момент? Т.е. куда бы вы добавили строчку _host.Click -= HostClicked?


S>>в финализатор


Q>Финализатор не предназначен для этих целей.


Q>Кроме того, у тебя нет гарантий относительно того, финализирован ли уже объект, на который ссылается _host.


я предлагал сделать отписку от ивента в финализатора Client, так что если мы находимся внутри, то мы понимаем, что объект финализирован, верно?
Re[5]: Финализатор
От: Qbit86 Кипр
Дата: 20.02.15 12:56
Оценка:
Здравствуйте, snaphold, Вы писали:

Q>>Кроме того, у тебя нет гарантий относительно того, финализирован ли уже объект, на который ссылается _host.

S>я предлагал сделать отписку от ивента в финализатора Client, так что если мы находимся внутри, то мы понимаем, что объект финализирован, верно?

Порядок вызова финализаторов не определён. Поэтому в финалзаторе Client в общем случае нельзя просто так обращаться к другим объектам. Если _host тоже финализируем (пусть не в этом конкретном случае, но вообще), и уже отправлен в мусор, то его финализатор может быть вызван как до, так и после финализатора твоего Client.

S>>>в финализатор


«When designing a type it is best to avoid using a Finalize method. There are several reasons for this »
Глаза у меня добрые, но рубашка — смирительная!
Re[5]: Финализатор
От: Sinix  
Дата: 20.02.15 14:26
Оценка:
Здравствуйте, snaphold, Вы писали:

S>я предлагал сделать отписку от ивента в финализатора Client, так что если мы находимся внутри, то мы понимаем, что объект финализирован, верно?

По сценарию Host держит ссылки на всех клиентов и сам хост не собран. Как тут поможет финализатор?
Re: Утечка памяти
От: TK Лес кывт.рф
Дата: 20.02.15 19:43
Оценка:
Здравствуйте, snaphold, Вы писали:

S>You might expect that after CreateClients finishes executing, the 1,000 Client
S>objects will become eligible for collection. Unfortunately, each client has another
S>referee: the _host object whose Click event now references each Client instance.
S>This may go unnoticed if the Click event doesn’t fire—or if the HostClicked method
S>doesn’t do anything to attract attention.


S>не понимаю откуда тут взятся утечке. Что мешает объекту Client почистить после себя Host?


Никто не мешает. Но, никто и не заставляет. Тут сам код "кривой" — разумнее рассчитывать на то, что любой объект переданный в конструкторе — передан во владение т.к. забрать обратно его уже нельзя. Если возникает утечка то, тут надо не добавлять отписку, а менять сам подход.

S>2


S>

S>Unfortunately, instances of Foo can never be garbage-collected! The problem is
S>the .NET Framework itself holds references to active timers so that it can fire their
S>Elapsed events. Hence:
S>• The .NET Framework will keep _timer alive.
S>• _timer will keep the Foo instance alive, via the tmr_Elapsed event handler.


S>ну как бы да, этот объект вечноживущий, но это не означает утечку памяти.


Для большей наглядности могли поле _timer и не заводить... Мало ли кто подумает, что оно на что-то влияет.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[2]: Опциональное владение
От: Qbit86 Кипр
Дата: 20.02.15 21:00
Оценка:
Здравствуйте, TK, Вы писали:

TK>Тут сам код "кривой" — разумнее рассчитывать на то, что любой объект переданный в конструкторе — передан во владение т.к. забрать обратно его уже нельзя.


Адаптеры потоков (Stream adapters) из стандартной библиотеки получают в конструктор булевский признак владения передаваемым низлежащим потоком.
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Опциональное владение
От: Sharov Россия  
Дата: 20.02.15 23:08
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Адаптеры потоков (Stream adapters) из стандартной библиотеки получают в конструктор булевский признак владения передаваемым низлежащим потоком.


Можно пример, а то первый раз слышу
Кодом людям нужно помогать!
Re[4]: Утечка памяти
От: Sharowarsheg  
Дата: 20.02.15 23:56
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>>утечка процесс неконтролируемого уменьшения памяти

S>Нет. Утечка — неосвобождение памяти.

почему? Вот, например, создал я при запуске консоль, а к ней буфер. И не освобождаю его, пока программа не закроется, потому что пока программа работает — нужна консоль, а когда программа закроется — система почистит.

Так что вот тебе неосвобождение памяти, но не утечка.

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