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

Язык С#: Сбор мусора, SuppressFinalize

Я читаю "Язык С#", 4-е издание, он рассказывает о сборке мусора, как показано ниже:

"BILL WAGNER: Следующее правило является важным различием между С# и другими управляемыми средами.

До завершения приложений вызывается деструктор для всех его объектов, которые еще не были собраны в мусор, если только такая очистка не была подавлена ​​(например, вызовом метода библиотеки GC.SuppressFinalize).

Итак, у меня есть пара вопросов:

  • Q1

    . Почему .net отличается от других управляемых сред (я полагаю, это намекает на Java?) Здесь? Любая конкретная проблема дизайна?

  • Q2

    . Что произойдет с объектами, вызываемыми GC.SuppressFinalize? Я понимаю, что это означает, что GC не будет вызывать финализатор таких объектов (деструктор), если это так, когда эти объекты будут действительно уничтожены, так что выделенные биты памяти будут возвращены в кучу? В противном случае произойдет утечка памяти?

4b9b3361

Ответ 1

Что произойдет с объектами, вызываемыми GC.SuppressFinalize? Я понимаю, что это означает, что GC не будет вызывать финализатор таких объектов (деструктор), если да, когда эти объекты будут действительно разрушены? в противном случае произойдет ли утечка памяти?

У вас есть непонимание того, для чего предназначена финализация. Завершение - очистка ресурсов, которые не управляются.

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

Поскольку какая-либо другая программа может захотеть получить доступ к этому файлу, вежливо закрыть файл как можно скорее. Но среда выполнения .NET не знает, что это целое имеет какое-то особое значение для операционной системы. Это просто целое число.

Как вы обычно решаете эту проблему, вы отмечаете объект как реализующий IDisposable, а затем вы вызываете "Dispose" на объект, как только вы закончите с ним. Затем ваша реализация "Dispose" закрывает файл.

Обратите внимание, что здесь ничего особенного не происходит. Это просто соглашение о том, что метод, который очищает неуправляемый ресурс, называется "Dispose", а объект, который должен быть удален, реализует IDisposable. Мусорная коллекция ничего не знает об этом.

Итак, теперь возникает проблема: что, если кто-то забывает вызывать Dispose? Сохраняется ли файл навсегда? (Очевидно, что файл будет закрыт, когда процесс завершится, но что делать, если процесс длится долго?)

Чтобы решить эту проблему, вы используете финализатор. Как это работает?

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

Ясно, что "Finalize" и "Dispose" часто нужно делать то же самое.

Но теперь возникает другая проблема. Предположим, вы выбрали объект. Теперь его не нужно дорабатывать. Завершение является дорогостоящим; он держит мертвый объект живым намного дольше, чем нужно. Поэтому, традиционно, когда вы размещаете объект, реализация Dispose не только закрывает неуправляемый ресурс, но и отмечает объект как "этот объект уже завершен, не завершайте его снова". Таким образом, он обманывает сборщик мусора, чтобы не помещать объект в очередь финализации.

Итак, давайте ответим на ваши конкретные вопросы:

Что произойдет с объектами, которые вызывается GC.SuppressFinalize?

Когда объект мертв, сборщик мусора просто восстанавливает память объекта, не помещая объект в очередь финализатора.

Я понимаю, что это означает, что GC не будет вызывать финализацию таких объектов

GC никогда не вызывает финализатор. Финализатор - это единственное, что вызывает финализаторы.

когда эти объекты будут действительно разрушены?

Неясно, что вы подразумеваете под "разрушенным". Если вы имеете в виду "когда будут финализаторы?" ответ "никогда", потому что вы сказали подавить финализацию. Если вы имеете в виду "когда будет восстановлена ​​память в управляемой куче?", Ответ будет "как только объект будет идентифицирован как мертвый сборщик мусора". Это произойдет раньше обычного, потому что объект не будет сохранен в очереди финализатора.

Ответ 2

Q1: Я подозреваю, потому что он больше заботится о достижении отличной производительности, в отличие от Java, которая принесла немало жертв за простоту.

Q2: Поскольку финализаторы даже не гарантируются, что их вызывают в первую очередь (даже если SuppressFinalize не существовало), это должно использоваться только по соображениям производительности, когда вы уже выбрали ресурсы. В противном случае вы должны использовать IDisposable для утилизации ресурсов.

Финализация!= Разрушение

Деструкторы (в смысле С++) не существуют в .NET - потому что каждый объект, в некотором смысле, имеет "деструктор", называемый сборщиком мусора.:)
То, что С# называет "деструкторами", на самом деле являются финализаторами. Финализаторы предназначены для размещения вещей, отличных от памяти, выделенной объектом, таких как дескрипторы файлов и т.д. Поэтому у каждого объекта не было финализатора. Память всегда освобождается GC, поэтому вы не получаете утечку памяти таким образом.

Ответ 3

Я знаю только второй ответ: SuppressFinalize вызывается, когда объект уже был разрушен, т.е. IDisposable.Dispose. Итак он не должен быть разрушен снова.

Это потому, что финализаторы - это очистка ресурсов, которая происходит в недетерминированное время. IDisposable был добавлен, чтобы обеспечить очистку ресурсов, которая происходит в определенное, предсказуемое время.

EDIT: При завершении процесса утечка памяти не имеет значения. Когда процесс завершается, его кучи собираются Windows, поэтому не имеет значения, возвращается ли память объекта в кучу или нет.

Ответ 4

Я пытаюсь ответить Q2:

Если ваш класс имеет финализатор (показано классом ClassName), он не будет непосредственно собран сборью мусора, а будет помещен в очередь финализатора, которая будет вызываться позже. Теперь, когда окончательный финализатор наконец называется, он обычно освобождает любые неуправляемые ресурсы, созданный класс. Если по какой-либо причине пользователь уже это сделал, обычно, вызывая dispose, финализатору не нужно очищать неуправляемую память, поэтому необходимо вызвать SuppressFinalizer. В противном случае он попытается снова освободить ресурсы. Таким образом, единственная утечка памяти, которую вы получите, - это любые ресурсы, которые вы запросили при создании своего класса. Финализатор должен просто освободить все, что еще не управляется инфраструктурой.

Ответ 5

Существует так, как только вызов GC.Collect() Объект с финализацией перейдет к следующему поколению хотя бы один раз.

Мы можем вызвать GC.SuppressFinalize, и GC узнает, что этот объект является де-ссылкой, мы можем удалить этот объект и кучу компакт-дисков.

Обычно это средство с шаблоном dispose