Язык C #: Сборка мусора, SuppressFinalize - PullRequest
16 голосов
/ 11 июля 2011

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

"БИЛЛ ВАГНЕР: Следующее правило является важным отличием C # от других управляемых сред.

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

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

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

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

Ответы [ 5 ]

39 голосов
/ 11 июля 2011

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

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

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

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

Способ решения этой проблемы обычно заключается в том, что вы помечаете объект как реализующий IDisposable, а затем вызываете «Dispose» для объекта, как только закончите с ним. Ваша реализация «Утилизация» затем закрывает файл.

Обратите внимание, что здесь ничего особенного не происходит. Это просто соглашение, что метод, который очищает неуправляемый ресурс, называется «Уничтожить», а объект, который должен быть удален, реализует IDisposable. Сборщик мусора абсолютно ничего не знает об этом.

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

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

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

Очевидно, что «Завершить» и «Распоряжаться» часто нужно делать одно и то же.

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

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

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

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

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

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

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

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

5 голосов
/ 11 июля 2011

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

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

Завершение! = Разрушение

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

2 голосов
/ 11 июля 2011

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

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

РЕДАКТИРОВАТЬ: При завершении процесса утечки памяти несущественны. Когда процесс завершается, его кучи собираются Windows, поэтому не имеет значения, возвращается ли память объекта в кучу или нет.

1 голос
/ 11 июля 2011

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

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

0 голосов
/ 10 января 2018

Это связано с тем, что после вызова GC.Collect () объект с finalize будет перемещен в следующее поколение как минимум один раз.

Мы можем вызвать GC.SuppressFinalize, и GC узнает, что этот объект разыменовантогда мы можем удалить этот объект и компактную кучу.

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

...