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

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

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

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

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

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

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

5 ответов

Решение

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

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

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

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

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

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

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

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

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

Ясно, что "Завершить" и "Утилизировать" часто нужно делать одно и то же.

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

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

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

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

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

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

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

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

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

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

Финализация!= Уничтожение

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

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

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

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

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

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

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

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

Как правило, это реализовать с шаблоном утилизации

Другие вопросы по тегам