Я родом из C ++ и проработал с C # около года. Как и многие другие, я не могу понять, почему детерминистическое управление ресурсами не является встроенным в язык.
Конструкция using
обеспечивает «детерминистическое» управление ресурсами и встроена в язык C #. Обратите внимание, что под «детерминистическим» я подразумеваю, что Dispose
гарантированно вызывается до того, как код после того, как начинает выполняться блок using
. Также обратите внимание, что это не то, что означает слово «детерминистический», но, похоже, каждый злоупотребляет им в этом контексте таким образом, что отстой.
В моем смещенном мозгу C ++ кажется, что использование умных указателей с подсчетом ссылок с детерминированными деструкторами - важный шаг вперед по сравнению с сборщиком мусора, требующим реализации IDisposable и вызова dispose для очистки ресурсов, не связанных с памятью.
Сборщик мусора не требует от вас реализации IDisposable
. Действительно, GC совершенно не замечает этого.
Правда, я не очень умен ... поэтому я спрашиваю это просто из желания лучше понять, почему вещи такие, какие они есть.
Отслеживание сборки мусора - это быстрый и надежный способ эмулировать машину с бесконечной памятью, освобождая программиста от бремени ручного управления памятью. Это устранило несколько классов ошибок (висячие указатели, освобождение слишком скоро, двойное освобождение, забыл освободить).
Что если C # был изменен так, что:
Объекты подсчитываются. Когда счетчик ссылок объекта становится равным нулю, метод очистки ресурса вызывается для объекта детерминистически,
Рассмотрим объект, совместно используемый двумя потоками. Потоки стремятся уменьшить счетчик ссылок до нуля. Один поток выиграет гонку, а другой будет отвечать за очистку. Это недетерминировано. Вера в то, что подсчет ссылок по своей природе является детерминированным, является мифом.
Другим распространенным мифом является то, что подсчет ссылок освобождает объекты в самой ранней точке программы. Это не так. Уменьшения всегда откладываются, как правило, до конца области. Это сохраняет объекты живыми дольше, чем необходимо, оставляя так называемый «плавающий мусор». Обратите внимание, что, в частности, некоторые трассирующие сборщики мусора могут перерабатывать объекты раньше, чем реализации подсчета ссылок на основе области.
тогда объект помечается для сборки мусора. Сборка мусора происходит в какое-то недетерминированное время в будущем, когда память восстанавливается. В этом случае вам не нужно реализовывать IDisposable или не забывать вызывать Dispose.
В любом случае вам не нужно реализовывать IDisposable
для объектов, собираемых мусором, так что это бесполезно.
Вы просто реализуете функцию очистки ресурсов, если у вас есть ресурсы, не связанные с памятью, для освобождения.
Почему это плохая идея?
Наивный подсчет ссылок очень медленный и имеет утечки циклов. Например, Boost shared_ptr
в C ++ в 10 раз медленнее, чем трассировка OCaml GC . Даже наивный подсчет ссылок на основе контекста не является детерминированным в присутствии многопоточных программ (а это почти все современные программы).
Будет ли это побеждать цель сборщика мусора?
Совсем нет, нет. На самом деле это плохая идея, которая была изобретена в 1960-х годах и подверглась интенсивному академическому изучению в течение следующих 54 лет, и в общем случае подсчет ссылок отстой.
Возможно ли реализовать такую вещь?
Абсолютно. Ранние прототипы .NET и JVM использовали подсчет ссылок. Они также нашли, что это высосано и отбросило это в пользу отслеживания GC.
РЕДАКТИРОВАТЬ: Из комментариев пока, это плохая идея, потому что
GC быстрее без подсчета ссылок
Да. Обратите внимание, что вы можете сделать подсчет ссылок намного быстрее, откладывая увеличение и уменьшение счетчика, но это жертвует детерминизмом, которого вы так жаждете, и он все еще медленнее, чем отслеживание GC с сегодняшними размерами кучи. Однако подсчет ссылок асимптотически быстрее, поэтому в какой-то момент в будущем, когда куча станет очень большой, возможно, мы начнем использовать RC в производственных решениях для автоматизированного управления памятью.
проблема обращения с циклами в графе объектов
Пробное удаление - это алгоритм, специально разработанный для обнаружения и сбора циклов в системах с подсчетом ссылок. Однако он медленный и недетерминированный.
Я думаю, что номер один действителен, но номер два легко справиться с использованием слабых ссылок.
Называть слабые ссылки «легким» - это победа надежды над реальностью. Это кошмар. Они не только непредсказуемы и сложны в разработке, но и загрязняют API.
Так что оптимизация скорости перевешивает минусы, которые вы:
может не освободить ресурс без памяти своевременно
Не using
своевременно освобождает ресурсы без памяти?
может освободить ресурс без памяти слишком рано
Если ваш механизм очистки ресурсов детерминирован и встроен в язык, вы можете исключить эти возможности.
Конструкция using
является детерминированной и встроена в язык.
Я думаю, что вы действительно хотите задать вопрос, почему IDisposable
не использует подсчет ссылок. Мой ответ анекдотичен: я использую языки сборки мусора в течение 18 лет, и мне никогда не приходилось прибегать к подсчету ссылок. Следовательно, я предпочитаю более простые API, которые не загрязнены случайной сложностью, такие как слабые ссылки.