Итак, я читал вопрос о том, как заставить сборщик мусора в C # запускать, когда почти все ответы одинаковы: вы можете сделать это, но не должны - за исключением некоторых очень редких случаев . К сожалению, никто не уточняет, что такое такие случаи.
Можете ли вы сказать мне, по какому сценарию на самом деле хорошая или разумная идея форсировать сборку мусора?
Я не спрашиваю о конкретных случаях C #, а о всех языках программирования, которые имеют сборщик мусора. Я знаю, что вы не можете заставить GC работать на всех языках, например на Java, но давайте предположим, что вы можете.
Ответы:
Вы действительно не можете делать общие заявления о подходящем способе использования всех реализаций GC. Они сильно различаются. Поэтому я поговорю с .NET, на который вы изначально ссылались.
Вы должны знать поведение GC довольно близко, чтобы сделать это с любой логикой или причиной.
Единственный совет по сбору, который я могу дать: никогда не делай этого.
Если вы действительно знаете сложные детали GC, вам не понадобится мой совет, так что это не будет иметь значения. Если вы еще не уверены со 100% уверенностью, что это поможет, вам придется поискать в Интернете и найти ответ, подобный следующему: вам не следует звонить в GC.Collect или, наоборот: вам следует узнать подробности работы GC. внутри и снаружи, и только тогда вы узнаете ответ .
Есть одно безопасное место, в котором есть смысл использовать GC.Collect :
GC.Collect - это доступный API, который вы можете использовать для профилирования времени. Вы могли бы профилировать один алгоритм, собирать и профилировать другой алгоритм сразу после этого, зная, что GC первого алгоритма не возникало во время вашего второго искажения результатов.
Такого рода профилирование - единственный раз, когда я советую собирать вручную кому-либо.
Во всяком случае, надуманный пример
Один из возможных вариантов использования - если вы загружаете действительно большие вещи, они окажутся в куче больших объектов, которая перейдет прямо к поколению 2, хотя опять же поколение 2 предназначено для долгоживущих объектов, потому что оно собирает реже Если вы знаете, что по какой-либо причине вы загружаете недолговечные объекты в Gen 2, вы можете очистить их быстрее, чтобы уменьшить размер Gen 2 и ускорить его сбор.
Это лучший пример, который я могу придумать, и он не очень хорош - давление LOH, которое вы создаете здесь, будет вызывать более частые коллекции, и коллекции настолько часты, насколько это возможно, - скорее всего, это будет очищать LOH так же, как так быстро, как вы взрывали его временными предметами. Я просто не доверяю себе, чтобы предполагать лучшую частоту сбора, чем сам GC - настроенный людьми, намного умнее меня.
Итак, давайте поговорим о некоторых семантиках и механизмах в .NET GC ... или ...
Все, что я думаю, я знаю о .NET GC
Пожалуйста, все, кто найдет здесь ошибки - исправьте меня. Хорошо известно, что большая часть GC - черная магия, и хотя я пытался опустить детали, в которых я не был уверен, я, вероятно, все еще ошибался.
Ниже преднамеренно упущены многочисленные детали, в которых я не уверен, а также гораздо больший объем информации, о которой я просто не знаю. Используйте эту информацию на свой страх и риск.
GC Concepts
.NET GC возникает в противоречивые моменты, поэтому он называется «недетерминированным», это означает, что вы не можете рассчитывать на то, что он произойдет в определенное время. Это также сборщик мусора поколений, что означает, что он разбивает ваши объекты на количество пройденных GC-проходов.
Объекты в куче Gen 0 прошли через 0 коллекций, они были созданы недавно, так как с момента их создания коллекция не возникла. Объекты в вашей куче Gen 1 пережили один проход сбора, и объекты в вашей куче Gen 2 пережили 2 прохода сбора.
Теперь стоит отметить причину, по которой он квалифицирует эти конкретные поколения и разделы соответственно. .NET GC распознает только эти три поколения, потому что проходы коллекции, проходящие через эти три кучи, немного отличаются. Некоторые объекты могут пережить коллекцию, пройденную тысячи раз. GC просто оставляет их на другой стороне раздела кучи Gen 2, нет никакого смысла разбивать их дальше, потому что они на самом деле Gen 44; передача коллекции на них такая же, как и в куче Gen 2.
У этих конкретных поколений есть семантические цели, а также реализованные механизмы, которые их соблюдают, и я расскажу об этом чуть позже.
Что в коллекции
Основная концепция этапа сбора GC состоит в том, что он проверяет каждый объект в пространстве кучи, чтобы увидеть, есть ли еще живые ссылки (корни GC) на эти объекты. Если для объекта найден корень GC, это означает, что выполняемый в настоящее время код все еще может достичь и использовать этот объект, поэтому его нельзя удалить. Однако, если корень GC не найден для объекта, это означает, что запущенный процесс больше не нуждается в объекте, поэтому он может удалить его, чтобы освободить память для новых объектов.
Теперь, после того как он завершит очистку группы объектов и оставит некоторые в покое, возникнет неприятный побочный эффект: свободные промежутки между живыми объектами, где были удалены мертвые. Эта фрагментация памяти, если оставить ее в покое, просто приведет к потере памяти, поэтому коллекции обычно делают так называемое «сжатие», когда они берут все оставшиеся живые объекты и сжимают их вместе в куче, чтобы свободная память была непрерывной на одной стороне кучи для Gen 0.
Теперь, учитывая идею трех кучи памяти, все разделены по количеству проходов сбора, которые они пережили, давайте поговорим о том, почему существуют эти разделы.
Коллекция Gen 0
Gen 0, будучи абсолютно новыми объектами, имеет тенденцию быть очень маленьким, так что вы можете безопасно собирать его очень часто . Частота гарантирует, что куча остается маленькой, а коллекции очень быстрыми, потому что они накапливаются в такой маленькой куче. Это основано более или менее на эвристике, которая утверждает: подавляющее большинство создаваемых вами временных объектов являются очень временными, поэтому временными они больше не будут использоваться или на них не будут ссылаться почти сразу после использования, и, следовательно, их можно собирать.
Коллекция Gen 1
Бытие 1, являющееся объектами, которые не попадают в эту очень временную категорию объектов, все еще может быть довольно недолгим, потому что большая часть созданных объектов не используется долго. Поэтому Gen 1 также собирает довольно часто, опять же, сохраняя кучу небольшого размера, поэтому сборы происходят быстро. Однако предположение менее из его объектов являются временными , чем Gen 0, поэтому он собирает реже , чем Gen 0
Я скажу, что, честно говоря, я не знаю технических механизмов, которые отличаются между проходом сбора Gen 0 и Gen 1, если они вообще существуют, кроме частоты, которую они собирают.
Gen 2 Collection
Gen 2 теперь должен быть матерью всех куч, не так ли? Ну да, это более или менее правильно. Это место, где живут все ваши постоянные объекты -
Main()
например, объект, в котором вы живете, и все, на чтоMain()
ссылаются, потому что они будут внедрены до тех пор, пока вы неMain()
вернетесь в конце вашего процесса.Учитывая, что Gen 2 - это ведро для всего, что не могли собрать другие поколения, его объекты в значительной степени постоянны или, по крайней мере, долго живут. Таким образом, признание очень небольшого количества того, что есть в Gen 2, на самом деле будет чем-то, что можно собирать, и не нужно собирать его часто. Это позволяет собирать данные медленнее, так как они выполняются гораздо реже. Так что именно здесь они взялись за все дополнительные варианты поведения для нечетных сценариев, потому что у них есть время на их выполнение.
Куча больших объектов
Одним из примеров дополнительного поведения Gen 2 является то, что он также выполняет сбор данных в куче больших объектов. До сих пор я полностью говорил о куче малых объектов, но среда выполнения .NET выделяет вещи определенного размера в отдельную кучу из-за того, что я назвал выше уплотнением. Сжатие требует перемещения объектов, когда коллекции заканчиваются в куче мелких объектов. Если в Gen 1 есть живой объект размером 10 МБ, ему понадобится гораздо больше времени, чтобы завершить уплотнение после сбора, что замедлит сбор данных в Gen 1. Таким образом, объект размером 10 Мб выделяется для кучи больших объектов и собирается во время второго поколения, которое выполняется так редко.
завершение
Другой пример - объекты с финализаторами. Вы помещаете финализатор на объект, который ссылается на ресурсы, выходящие за рамки .NET GC (неуправляемые ресурсы). Финализатор - это единственный способ, которым сборщик мусора может потребовать сбора неуправляемых ресурсов - вы реализуете свой финализатор для ручного сбора / удаления / освобождения неуправляемого ресурса, чтобы гарантировать, что он не просочится из вашего процесса. Когда GC приступит к выполнению финализатора ваших объектов, ваша реализация очистит неуправляемый ресурс, что сделает GC способным удалить ваш объект без риска утечки ресурса.
Механизм, с помощью которого финализаторы делают это, напрямую связан с очередью финализации. Когда среда выполнения выделяет объект с помощью финализатора, он добавляет указатель на этот объект в очередь финализации и блокирует ваш объект на месте (так называемое закрепление), так что сжатие не будет перемещать его, что нарушит ссылку на очередь финализации. По мере прохождения сбора, в конечном итоге будет обнаружено, что ваш объект больше не имеет корня GC, но для его сбора необходимо выполнить финализацию. Поэтому, когда объект мертв, коллекция будет перемещать свою ссылку из очереди завершения и помещать ссылку на него в так называемую очередь «FReachable». Затем коллекция продолжается. В другое «недетерминированное» время в будущем, отдельный поток, известный как поток финализатора, пройдет через очередь FReachable, выполняя финализаторы для каждого из объектов, на которые имеются ссылки. После завершения очередь FReachable становится пустой, и она слегка переворачивает заголовок каждого объекта, который говорит, что им не требуется финализация (этот бит также можно перевернуть вручную с помощью
GC.SuppressFinalize
что часто встречается вDispose()
методах), я также подозреваю, что это открепило объекты, но не цитируйте меня по этому поводу. Следующая коллекция, находящаяся в куче этого объекта, наконец соберет его. Коллекции Gen 0 даже не обращают внимания на объекты с этим битом, необходимым для финализации, он автоматически продвигает их, даже не проверяя их корень. Некорневый объект, нуждающийся в финализации в Gen 1, будет брошен вFReachable
очередь, но коллекция ничего с ним не делает, поэтому он живет в Gen 2. Таким образом, все объекты, которые имеют финализатор, и неGC.SuppressFinalize
будут собраны в Gen 2.источник
Я приведу несколько примеров. В общем, это редко, когда принуждение к ГК - это хорошая идея, но оно того стоит. Этот ответ основан на моем опыте использования .NET и GC литературы. Он должен хорошо распространяться на другие платформы (по крайней мере, те, которые имеют значительный сборщик мусора).
Если ваша цель - пропускная способность, чем реже GC, тем лучше. В этих случаях форсирование коллекции не может иметь положительного эффекта (за исключением довольно надуманных проблем, таких как увеличение использования кэша ЦП за счет удаления мертвых объектов, вкрапленных в живые объекты). Сбор партии более эффективен для всех знакомых мне коллекционеров. Для производственного приложения в стационарном режиме потребления памяти индукция ГХ не помогает.
Приведенные выше примеры нацелены на согласованность и ограниченность использования памяти. В этих случаях индуцированные GC могут иметь смысл.
Кажется, широко распространена идея, что GC - это божественная сущность, которая вызывает коллекцию всякий раз, когда это действительно оптимально. Ни один GC, о котором я знаю, не является настолько сложным, и действительно очень трудно быть оптимальным для GC. GC знает меньше, чем разработчик. Это эвристика основана на счетчиках памяти и таких вещах, как скорость сбора данных и так далее. Эвристики обычно хороши, но они не фиксируют внезапные изменения в поведении приложений, такие как освобождение больших объемов управляемой памяти. Он также не учитывает неуправляемые ресурсы и требования к задержке.
Обратите внимание, что затраты на сборку мусора зависят от размера кучи и количества ссылок в куче. На небольшой куче стоимость может быть очень маленькой. Я видел скорость сбора данных G2 с .NET 4.5, равную 1-2 ГБ / с, в производственном приложении с размером кучи 1 ГБ.
источник
Как правило, сборщик мусора будет собирать, когда он сталкивается с «нагрузкой на память», и считается хорошей идеей не собирать его в другое время, поскольку это может вызвать проблемы с производительностью или даже заметные паузы при выполнении вашей программы. И на самом деле, первая точка зависит от второй: по крайней мере, для сборщика мусора поколений он работает тем эффективнее, чем выше отношение мусора к хорошим объектам, поэтому, чтобы минимизировать время, затрачиваемое на паузу в программе , он должен откладывать и позволять мусору накапливаться как можно больше.
Подходящее время для ручного вызова сборщика мусора - это когда вы закончите делать что-то, что 1) может создать много мусора, и 2) ожидается, что пользователь займет некоторое время и оставит систему без ответа так или иначе. Классический пример в конце загрузки чего-то большого (документ, модель, новый уровень и т. Д.)
источник
Одна вещь, о которой никто не упомянул, - то, что, хотя Windows GC удивительно хороша, GC на Xbox - мусор (каламбур) .
Поэтому, когда вы пишете код для игры XNA, предназначенной для запуска на XBox, абсолютно необходимо выбрать подходящий момент для сбора мусора, иначе у вас будут ужасные периодические сбои FPS. Кроме того, в XBox обычно используют
struct
способ, гораздо чаще, чем обычно, чтобы минимизировать количество объектов, которые необходимо собирать мусором.источник
Сборка мусора - это прежде всего инструмент управления памятью. Таким образом, сборщики мусора будут собирать, когда есть давление памяти.
Современные сборщики мусора очень хороши и становятся лучше, поэтому вряд ли вы сможете улучшить их, собирая вручную. Даже если вы сможете что-то улучшить сегодня, вполне возможно, что дальнейшее усовершенствование выбранного вами сборщика мусора сделает вашу оптимизацию неэффективной или даже контрпродуктивной.
Однако сборщики мусора обычно не пытаются оптимизировать использование ресурсов, отличных от памяти. В средах со сборщиком мусора наиболее ценные ресурсы, не связанные с памятью, имеют
close
метод или аналогичный метод, но в некоторых случаях это не так по какой-то причине, например, совместимость с существующим API.В этих случаях может иметь смысл запускать сборку мусора вручную, когда вы знаете, что используется ценный ресурс, не связанный с памятью.
RMI
Одним конкретным примером этого является удаленный вызов метода Java. RMI - это библиотека удаленных вызовов процедур. Обычно у вас есть сервер, который делает различные объекты доступными для использования клиентами. Если сервер знает, что объект не используется никакими клиентами, тогда этот объект пригоден для сборки мусора.
Тем не менее, единственный способ, которым сервер знает об этом, - это когда клиент говорит об этом, а клиент только сообщает серверу, что ему больше не нужен объект, как только клиент соберет мусор, который его использует.
Это создает проблему, поскольку у клиента может быть много свободной памяти, поэтому сборка мусора может выполняться не очень часто. Между тем, на сервере может быть много неиспользуемых объектов в памяти, которые он не может собрать, потому что он не знает, что клиент их не использует.
Решение в RMI состоит в том, чтобы клиент периодически запускал сборку мусора, даже когда у него много свободной памяти, чтобы обеспечить своевременный сбор объектов на сервере.
источник
using
блок или иным образом вызыватьClose
метод для убедитесь, что ресурс удален как можно скорее. Использование GC для очистки ресурсов, не связанных с памятью, ненадежно и вызывает всевозможные проблемы (особенно с файлами, которые необходимо заблокировать для доступа, поэтому их можно открыть только один раз).close
метод доступен (или ресурс может использоваться сusing
блоком), это правильный подход. Ответ конкретно касается редких случаев, когда эти механизмы недоступны.В большинстве случаев лучше не форсировать сборку мусора. (Каждая система, над которой я работал, имела принудительную сборку мусора, подчеркивала проблемы, которые в случае ее устранения устраняли бы необходимость принудительной сборки мусора и значительно ускоряли работу системы.)
Есть несколько случаев, когда вы знаете больше об использовании памяти, чем сборщик мусора. Это вряд ли может быть правдой для многопользовательского приложения или службы, которая отвечает более чем на один запрос за раз.
Однако в некоторых типах обработки вы знаете больше, чем GC. Например, рассмотреть приложение, которое.
Вы можете быть в состоянии сделать случай (после тщательной проверки) , что вы должны заставить полный сбор мусора после того как вы обработку каждый файл.
Другой случай - это служба, которая просыпается каждые несколько минут для обработки некоторых элементов и не сохраняет состояния, пока она спит . Затем принуждая полную коллекцию непосредственно перед сном может быть полезным.
Я бы предпочел иметь API для сборки мусора, когда я мог бы дать ему подсказки об этом типе вещей, не заставляя себя собирать GC.
См. Также « Эксперты Рико Мариани »
источник
Есть несколько случаев, когда вы можете захотеть вызвать gc () самостоятельно.
gc()
вызова очень мало объектов останется, не говоря уже о том, чтобы перенести их в пространство старшего поколения. ] Когда вы собираетесь создать большую коллекцию объектов и использовать много памяти. Вы просто хотите очистить как можно больше места для подготовки. Это просто здравый смысл. При вызовеgc()
вручную не будет избыточной проверки ссылочного графа для части этой большой коллекции объектов, которые вы загружаете в память. Короче говоря, если вы запускаете,gc()
прежде чем загружать много в память,gc()
Индуцированное во время загрузки происходит меньше, по крайней мере, один раз, когда загрузка начинает создавать давление памяти.большойобъекты, и вы вряд ли загрузите больше объектов в память. Короче говоря, вы переходите от создания фазы к использованию фазы. При вызове, вgc()
зависимости от реализации, используемая память будет сжата, что значительно улучшает локальность кэша. Это приведет к значительному повышению производительности, которое вы не получите от профилирования .gc()
и реализация управления памятью поддержит, вы создадите намного лучшую непрерывность для вашей физической памяти. Это снова делает новую большую коллекцию объектов более непрерывной и компактной, что, в свою очередь, повышает производительностьисточник
By calling gc() depending on implementation, the memory in used will be compacted which massively improves cache locality. This will result in massive improve in performance that you will not get from profiling.
Если вы выделяете тонну объектов в строке, то вероятность того, что они уже сжаты. Во всяком случае, сборка мусора может немного перемешать их. В любом случае, использование структур данных, которые плотны и не перепрыгивают случайно в памяти, будет иметь большее влияние. Если вы используете наивный связанный список, состоящий из одного элемента на узел, никакие ручные хитрости GC не восполнят это.Пример из реальной жизни:
У меня было веб-приложение, которое использовало очень большой набор данных, которые редко менялись и к которым нужно было обращаться очень быстро (достаточно быстро для ответа на нажатие клавиши через AJAX).
Достаточно очевидная вещь, которую нужно сделать здесь, это загрузить соответствующий график в память и получить к нему доступ оттуда, а не к базе данных, обновляя график при изменении БД.
Но, будучи очень большой, наивная загрузка заняла бы по меньшей мере 6 ГБ памяти с данными из-за роста в будущем. (У меня нет точных цифр, как только стало ясно, что моя машина на 2 ГБ пытается справиться хотя бы с 6 ГБ, у меня были все необходимые измерения, чтобы знать, что она не будет работать).
К счастью, в этом наборе данных было большое количество неизменяемых эскимо объектов, которые были одинаковы; Как только я понял, что определенная партия была такой же, как другая, я мог использовать псевдоним одну ссылку на другую, позволяя собирать большое количество данных, и, следовательно, помещать все в менее чем половину концерта.
Все хорошо, но для этого все еще нужно перебрать более 6 ГБ объектов за полминуты, чтобы добраться до этого состояния. Оставленный сам по себе, GC не справился; скачок активности по сравнению с обычным шаблоном приложения (гораздо менее тяжелым по отношению к освобождению в секунду) был слишком резким.
Поэтому периодические вызовы
GC.Collect()
во время этого процесса сборки означали, что все это работало гладко. Конечно, я не вызывал вручнуюGC.Collect()
остальное время, пока приложение работает.Этот пример из реальной жизни является хорошим примером того, когда мы должны использовать
GC.Collect()
:Большую часть времени, когда я думал, что у меня может быть случай, когда
GC.Collect()
стоит позвонить, потому что применяются пункты 1 и 2, пункт 3 предполагает, что ситуация ухудшается или, по крайней мере, не становится лучше (и с небольшими или никакими улучшениями я бы склоняться к тому, чтобы не звонить по вызову, поскольку такой подход с большей вероятностью окажется лучше в течение срока службы приложения).источник
У меня есть использование для удаления мусора, что несколько необычно.
Эта ошибочная практика, которая, к сожалению, очень распространена в мире C #, заключается в реализации удаления объектов с использованием уродливой, неуклюжей, не элегантной и склонной к ошибкам идиомы, известной как IDisposable-dispose . MSDN описывает это в деталях , и многие люди клянутся им, следуют этому религиозно, часами часами обсуждают, как именно это должно быть сделано, и т. Д.
(Обратите внимание, что то, что я называю здесь уродливым, - это не сам шаблон удаления объектов; то, что я называю уродливым, - это особая
IDisposable.Dispose( bool disposing )
идиома.)Эта идиома была придумана, потому что якобы невозможно гарантировать, что деструктор ваших объектов всегда будет вызываться сборщиком мусора для очистки ресурсов, поэтому люди выполняют очистку ресурсов внутри
IDisposable.Dispose()
, а в случае, если они забывают, они также дают ему еще одну попытку от в деструкторе. Вы знаете, на всякий случай.Но тогда у вас
IDisposable.Dispose()
могут быть как управляемые, так и неуправляемые объекты для очистки, но управляемые объекты не могут быть очищены приIDisposable.Dispose()
вызове из деструктора, потому что они уже были обработаны сборщиком мусора на тот момент, поэтому это необходимость в отдельномDispose()
методе, который принимаетbool disposing
флаг, чтобы знать, следует ли очищать как управляемые, так и неуправляемые объекты или только неуправляемые.Извините, но это просто безумие.
Я придерживаюсь аксиомы Эйнштейна, которая гласит, что все должно быть как можно проще, но не проще. Ясно, что мы не можем пропустить очистку ресурсов, поэтому самое простое из возможных решений должно включать как минимум это. Следующее простейшее решение заключается в том, чтобы всегда утилизировать все в точное время, которое предполагается утилизировать, не усложняя ситуацию, полагаясь на деструктора в качестве альтернативы.
Строго говоря, сейчас, конечно, невозможно гарантировать, что ни один программист никогда не совершит ошибку, забыв вызвать
IDisposable.Dispose()
, но мы можем использовать деструктор, чтобы уловить эту ошибку. На самом деле это очень просто: все, что нужно сделать деструктору, - это сгенерировать запись в журнале, если он обнаружит, чтоdisposed
флаг одноразового объекта никогда не был установленtrue
. Таким образом, использование деструктора не является неотъемлемой частью нашей стратегии утилизации, но это наш механизм обеспечения качества. И поскольку это тест только в режиме отладки, мы можем поместить весь наш деструктор в#if DEBUG
блок, поэтому мы никогда не понесем штрафов за разрушение в производственной среде. (IDisposable.Dispose( bool disposing )
Идиома предписывает, чтоGC.SuppressFinalize()
следует вызывать именно для того, чтобы уменьшить накладные расходы на завершение, но с моим механизмом можно полностью избежать этих накладных расходов на производственную среду.)То, к чему он сводится, - это аргумент вечной жесткой ошибки против мягкой ошибки :
IDisposable.Dispose( bool disposing )
идиома - это подход мягкой ошибки, и он представляет собой попытку позволить программисту забыть вызыватьDispose()
без сбоя системы, если это возможно. Подход с жесткой ошибкой говорит, что программист всегда должен быть уверен, чтоDispose()
это будет вызвано. Наказание, обычно предписываемое подходом с жесткой ошибкой, в большинстве случаев является ошибкой утверждения, но для этого конкретного случая мы делаем исключение и уменьшаем штраф до простой выдачи записи в журнале ошибок.Таким образом, чтобы этот механизм работал, версия нашего приложения DEBUG должна выполнить полное удаление мусора перед выходом, чтобы гарантировать, что все деструкторы будут вызваны, и, таким образом, перехватывать любые
IDisposable
объекты, которые мы забыли удалить.источник
Now, strictly speaking, it is of course impossible to guarantee that no programmer will ever make the mistake of forgetting to invoke IDisposable.Dispose()
На самом деле это не так, хотя я не думаю, что C # способен на это. Не выставляйте ресурс; вместо этого предоставьте DSL для описания всего, что вы будете с ним делать (в основном, монаду), а также функцию, которая получает ресурс, делает вещи, освобождает его и возвращает результат. Хитрость заключается в том, чтобы использовать систему типов, чтобы гарантировать, что, если кто-то переправит ссылку на ресурс, его нельзя будет использовать при другом вызове функции run.Dispose(bool disposing)
(которая не определена вIDisposable
том, что она используется для очистки как управляемых, так и неуправляемых объектов, которые объект имеет как поле (или иным образом отвечает за него), что решает неправильную проблему. Если вы переносите все неуправляемые объекты в управляемом объекте без других одноразовых объектов, о которых можно беспокоиться, тогда всеDispose()
методы будут либо одним из них (если финализатор будет выполнять такую же очистку при необходимости), либо будут иметь только управляемые объекты для удаления (не иметь финализатор на всех), и необходимостьbool disposing
исчезаетdispose(disposing)
идиома «ужасный», но я говорю так, потому что люди так часто используют эту технику и финализаторы, когда у них есть только управляемые ресурсы (DbConnection
например, объект управляется , он не привязан или не прокомментирован), и ВЫ ДОЛЖНЫ ТОЛЬКО КОГДА-ЛИБО РЕАЛИЗОВАТЬ ФИНАЛИЗАТОР С НЕПРАВИЛЬНЫМ, ЗАКРЫТЫМ, КОМ-МАРШАЛЛИЗИРОВАННЫМ ИЛИ НЕ БЕЗОПАСНЫМ КОДОМ . Я подробно описал в своем ответе, как ужасно дороги финализаторы, не используйте их, если у вас нет неуправляемых ресурсов в вашем классе.dispose(dispoing)
идиоме, но правда в том, что это настолько распространено, потому что люди так боятся GC, что что-то не связанное с это (dispose
должно быть, имеет отношение к ГК) заслуживает того, чтобы они просто принимали назначенное лекарство, даже не исследуя его. Хорошо, что вы осмотрели его, но вы пропустили самое большое целое (это поощряет финализаторов farrr чаще, чем они должны быть)Говоря очень теоретически и игнорируя такие проблемы, как некоторые реализации GC, замедляющие работу во время циклов сбора, самый большой сценарий, который я могу придумать для принудительного сбора мусора, - это программное обеспечение для решения критически важных задач, где логические утечки предпочтительнее, чем зависания висячих указателей, например, из-за сбоя в неожиданные времена может стоить человеческих жизней или чего-то в этом роде.
Если вы посмотрите на некоторые из нездоровых инди-игр, написанных с использованием языков GC, таких как Flash-игры, они просачиваются как сумасшедшие, но не вылетают. Они могут занимать в десять раз больше памяти, чем 20 минут, чтобы начать игру, потому что какая-то часть кодовой базы игры забыла установить ссылку на ноль или удалить ее из списка, и частота кадров может начать страдать, но игра все еще работает. Подобная игра, написанная с использованием некачественного кодирования на C или C ++, может дать сбой в результате доступа к висящим указателям в результате ошибки управления ресурсами того же типа, но она не будет сильно утекать.
Для игр сбой может быть предпочтительнее в том смысле, что его можно быстро обнаружить и исправить, но для критически важной программы сбой в совершенно неожиданное время может кого-нибудь убить. Таким образом, я думаю, что основными случаями будут сценарии, в которых безопасность или отсутствие каких-либо других форм безопасности абсолютно необходимы, а логическая утечка - сравнительно тривиальная вещь по сравнению.
Основной сценарий, когда я думаю, что принудительно использовать GC - это плохо, когда логическая утечка на самом деле менее предпочтительна, чем сбой. Например, в играх сбой не обязательно кого-нибудь убьет, и его легко можно поймать и исправить во время внутреннего тестирования, в то время как логическая утечка может остаться незамеченной даже после того, как продукт выйдет, если он не настолько серьезен, что делает игру недоступной в течение нескольких минут. , В некоторых областях легко воспроизводимый сбой, возникающий при тестировании, иногда предпочтительнее утечки, о которой никто не замечает сразу.
Еще один случай, когда я могу подумать о том, где может быть предпочтительнее использовать GC в команде, - это программа с очень коротким сроком действия, например, просто выполняемая из командной строки, которая выполняет одну задачу, а затем выключается. В этом случае время жизни программы слишком короткое, чтобы сделать любую логическую утечку нетривиальной. Логические утечки, даже для больших ресурсов, обычно становятся проблемными только через несколько часов или минут после запуска программного обеспечения, поэтому у программного обеспечения, которое предназначено для выполнения только в течение 3 секунд, вряд ли когда-либо возникнут проблемы с логическими утечками, и это может многое сделать проще писать такие недолговечные программы, если команда просто использовала GC.
источник