Подтвердить что ты не робот

Как убедить моих коллег не внедрять IDisposable во всем?

Я работаю над проектом, в котором существует огромное количество объектов, создаваемых несколькими классами, которые остаются в памяти на всю жизнь приложения. Есть много утечек памяти, вызванных тем, что OutOfMemoryExceptions бросаются время от времени. Похоже, что после того, как объекты, созданные из объектов, были недоступны, они не собираются мусором.

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

Решение, предложенное моими коллегами, следующее: Внедрить IDisposable для всех классов, по всем направлениям и в методе Dispose, обнулить все ссылки в ваших объектах и ​​отделить все события, к которым вы присоединились.

Я считаю, что это действительно очень плохая идея. Во-первых, потому что он "перебор", поскольку проблема может быть решена главным образом путем исправления нескольких проблемных областей, а во-вторых, поскольку целью IDisposable является освобождение любых неуправляемых ресурсов, которыми управляют ваши объекты, а не потому, что вы не доверяете сборщику мусора. До сих пор мои аргументы не угасали. Как я могу убедить их, что это бесполезно?

4b9b3361

Ответ 1

По совпадению я просто разместил этот комментарий в другом месте:

Ссылка на объект, являющийся неправильно сохраненный утечка ресурсов. Вот почему программы GC все еще могут иметь утечки, обычно из-за шаблон наблюдателя - наблюдатель в списке вместо наблюдаемого и никогда не снимается с него. В конечном счете, remove требуется для каждого add, просто поскольку a delete требуется для каждого new. Точно такая же ошибка программирования, вызывая точно такую ​​же проблему. "ресурс" - это действительно пара функций, которые нужно называть равное количество раз с соответствующие аргументы и "утечка ресурсов" - это то, что происходит, когда вы не можете этого сделать.

И вы говорите:

целью IDisposable является освобождение любого Неуправляемые ресурсы ваших объектов элементы управления

Теперь операторы += и -= в событии фактически представляют собой пару функций, которые вы должны вызывать равное количество раз с соответствующими аргументами (пара событий/обработчик является соответствующими аргументами).

Поэтому они составляют ресурс. И поскольку они не рассматриваются (или "управляются" ) GC для вас, может быть полезно подумать о них как о простом неуправляемом ресурсе. Как отмечает Джон Скит в комментариях, неуправляемый обычно имеет определенное значение, но в контексте IDisposable я считаю полезным расширить его, включив в него что-то вроде ресурса, которое должно быть "снесено" после того, как оно было "построено".

Таким образом, отключение событий - очень хороший кандидат для обработки с помощью IDisposable.

Конечно, вам нужно называть Dispose где-то, и вам не нужно реализовывать его на каждом отдельном объекте (только те, у которых есть отношения событий, которым требуется управление).

Кроме того, имейте в виду, что если пара объектов связана событием, и вы "бросаете их по течению", теряя при этом все ссылки на них во всех других объектах, они не поддерживают друг друга живыми. GC не использует подсчет ссылок. Когда объект (или остров объектов) недоступен, он должен быть собран.

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

Ответ 2

Направьте их на Joe Duffy опубликовать сообщение о IDisposable/finalizers - объединенная мудрость многих умных людей.

В настоящее время мне трудно увидеть выражение, в котором говорится: "Не реализуйте его, когда вам это не нужно" - но помимо всего прочего, показывая им, что сложность, связанная с его реализацией, вполне может помочь отговорить их от него...

К сожалению, если люди не будут слушать, они не будут слушать. Попытайтесь заставить их объяснить, почему они думают, что им нужно IDisposable. Думают ли они, что сборщик мусора не работает? Покажите им, что это работает. Если вы можете убедить их в том, что он не делает ничего хорошего (для большинства типов), то, конечно же, они перестанут добавлять работу для себя...

Как говорит Брайан, реализация IDisposable сама по себе не поможет справиться с проблемой события - ее нужно на самом деле называть чем-то. Финализаторы также не помогут вам в этом случае. Им действительно нужно явно сделать что-то, чтобы удалить обработчики событий.

Ответ 3

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

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

Однако я бы не рекомендовал использовать IDisposable для всех ваших типов, просто потому, что это не имеет смысла. Интерфейс используется для указания того, что рассматриваемый тип использует некоторый ресурс, который не обрабатывается сборщиком мусора.

Ссылки на события обрабатываются сборщиком мусора. Вам просто нужно отказаться от подписки, если ваш издатель живет значительно дольше, чем ваши подписчики. Как только издатель умрет, подписчики тоже умрут.

Ответ 4

Я однажды помог коллеге решить аналогичную проблему с ошибками OutOfMemoryException (вызванными событиями, оставляющими ссылки на объекты). Первое, что я сделал, - запустить код через FXCop, который выделил Dispose, который не вызывался в классе IDisposable.

Изменение этого кода, подлежащего удалению, устраняет проблему. Возможно, вы должны рекомендовать использовать FXCop?

Возможно, найдите код с проблемой в исходном репозитории, запустите FXCop на нем - посмотрите, выделяет ли он проблему (возможно, это будет вызвано классом .NET Framework) и используйте это, чтобы убедить своих сотрудников.

Ответ 5

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

Ответ 6

Спросите их, хотят ли они, чтобы они были вынуждены отключить двигатель после использования велосипеда, даже если нет двигателя.

Или, если они хотели бы, чтобы нажимали кнопку "выключить" на своем стуле, столе, кофейной чашке и других вещах до того, как они покинули свое рабочее место, даже если ничего не нужно отключать.

Реализация IDisposable заставляет пользователя явно указывать объект, когда он больше не используется. Если этот объект не нуждается в очистке, это просто лишняя сложность.

Кстати, ИМХО, не регистрирующее события путем реализации IDisposable, является подходящим способом очистки событий. Есть что-то "выключить".

Ответ 7

IDisposable предназначен только для выпуска неуправляемых ресурсов (SafeHandles и т.п.), а метод Dispose распространяется через иерархию классов. Это не предназначено, чтобы попытаться обойти плохие неуправляемые методы программирования.

Ответ 8

Создать обязанность при разрешении еще одного исключения OutOfMemoryException. Когда кто-то разрешает ошибки принадлежат другим, он поднимает ответственность за собственный код. На раннем этапе нашего проекта нам рекомендовали "флаг дурака" - ответственный за фатальную ошибку получает этот флаг за день.

Ответ 9

Предложите решение, намного превосходящее их решение, -)

Например, простой альтернативой может быть использование WeakReference в объектах long-living для хранения ссылок на обработчики событий. Это потребует, чтобы обработчики событий ссылались где-то в другом месте до тех пор, пока они нужны, но как только они выйдут из области видимости, они будут собраны в мусор, и слабые ссылки могут быть удалены из долгоживущих объектов.

Ответ 10

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

Разве это не означает, что IDisposable? Зачем вам нужно другое решение, если оно уже есть? И почему эти костяшки не правильно реализовали свои объекты в первую очередь?;)

Ответ 11

Объект должен реализовать IDisposable, если ему нужно убедиться, что что-то вне себя, которое может пережить его, очищается до отказа. Некоторые свойства объектов "подключены" к другим объектам, поэтому изменение этих свойств приведет к изменению других объектов; иногда необходимо установить для этих свойств значение null. В vb.net поле "WithEvents" действительно является свойством, которое прикрепляет и отделяет обработчики событий, и, следовательно, поля WithEvents в vb.net должны иметь значение Nothing. Обратите внимание, что обычно не полезно, чтобы объект реализовал IDisposable исключительно с целью обнуления собственных полей. Есть несколько случаев, когда это может быть полезно (например, если объект, который был в течение длительного времени, содержит ссылку на недавно созданный объект, очистка ссылки может позволить собирать более недавно созданный объект раньше, чем это было бы в противном случае), но это, безусловно, не нужно.

Необходимо обеспечить, чтобы объекты, которые нуждались в очистке других объектов, реализовали IDisposable и обеспечили очистку этих других объектов. Я рад, что Microsoft заставляет людей писать код, который отказывается от обработчиков событий. Хотя верно, что можно отказаться от отказа от обработчиков событий в тех случаях, когда издатели событий не ожидают подписчиков, и такие случаи обычно происходят с взаимосвязанными элементами GUI, я действительно не вижу причины, по которой событие не всегда должно быть очищается, как само собой разумеющееся.