Коли я повинен використовувати GC.SuppressFinalize ()?


287

У .NET, за яких обставин я повинен використовувати GC.SuppressFinalize()?

Яку перевагу надає мені цей метод?


Я бачив кілька запитань щодо фіналізаторів та IDisposable, stackoverflow також повинен мати щось про GC.SupressFinalize та слабкі посилання
Сем Сафрон

Я не думаю, що слабкі посилання багато нічого не стосуються фіналізаторів - можливо, ви повинні поставити більш пряме запитання щодо них.
Майкл Берр

Yerp Я мав намір поставити окреме запитання про слабкі рефлекси, все це може зв'язатись разом, коли ви будуєте об'єктивні пули. Також я повинен задати питання про відродження об'єктів ala ReRegisterForFinalize
Сем Сафрон

Відповіді:


296

SuppressFinalizeслід викликати лише клас, який має фіналізатор. Про це він інформує збирач сміття (GC)this об’єкт було очищено повністю.

Рекомендована IDisposableсхема, коли у вас є фіналізатор:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

Зазвичай CLR зберігає вкладки на об'єкти з фіналізатором, коли вони створюються (що робить їх дорожчими для створення). SuppressFinalizeповідомляє GC, що об'єкт очищено належним чином і йому не потрібно переходити до черги фіналізатора. Це схоже на деструктор C ++, але не діє як щось подібне.

SuppressFinalizeОптимізація не є тривіальною, так як ваші об'єкти можуть жити довгий час очікування в черзі фіналізатор. Не спокушайтесь зателефонувати SuppressFinalizeна інші об'єкти. Це серйозний дефект, який чекає, що трапиться.

Вказівки щодо дизайну повідомляють нам, що фіналізатор не потрібен, якщо ваш об’єкт реалізується IDisposable, але якщо у вас є фіналізатор, ви повинні реалізувати, IDisposableщоб дозволити детерміновану очистку свого класу.

Більшу частину часу ви повинні мати можливість піти, IDisposableщоб очистити ресурси. Вам потрібен фіналізатор лише тоді, коли ваш об’єкт тримається на некерованих ресурсах і вам потрібно гарантувати, що ці ресурси очищені.

Примітка: Іноді кодери додають фіналізатор для налагодження збірок власних IDisposableкласів, щоб перевірити, чи правильно код розмістив їх IDisposableоб'єкт.

public void Dispose() // Implement IDisposable
{
    Dispose(true);
#if DEBUG
    GC.SuppressFinalize(this);
#endif
}

#if DEBUG
~MyClass() // the finalizer
{
    Dispose(false);
}
#endif

1
У першому фрагменті коду я просто розміщую, як виглядає рекомендований шаблон IDisposable + finalizer. Код налагодження хороший, але він може відволікати. .. Я можу лише рекомендувати уникати фіналізаторів, за винятком класів, які мають некеровані ресурси. Написання безпечного коду фіналізатора нетривіально.
Роберт Полсон,

1
Привіт, Чому нам потрібно викликати dispose with false як параметр з фіналізатора? Що робити, якщо утилізацію ніколи не називають, і тоді вона не буде розпоряджатися? Що робити, якщо ми просто перевіримо, чи об’єкт був утилізований чи ні, і зробимо фактичну очистку.
Сонник

3
@Dreamer - це залежить від вашої реалізації. Загалом, ви хочете дізнатися, чи викликає Dispose фіналізатор проти реалізації IDisposable.Dispose (). Якщо вас зателефонували з фіналізатора, ви повинні припустити, що приватні посилання більше не дійсні, і ви дійсно не можете зробити багато. Якщо ж зателефонували з IDisposable.Dispose (), ви знаєте, що посилання все ще дійсні.
Роберт Поулсон

32
Якщо реалізація класу IDisposableвідсутня sealed, він повинен включати виклик до, GC.SuppressFinalize(this) навіть якщо він не включає визначений користувачем фіналізатор . Це необхідно для забезпечення належної семантики для похідних типів, які додають визначений користувачем фіналізатор, але лише перекривають захищений Dispose(bool)метод.
Сем Харвелл

1
Якщо це не було sealedзазначено @SamHarwell, це важливо для похідних класів. CodeAnalysis призводить до ca1816 + ca1063, коли клас не є герметичним, але запечатані класи непогані SuppressFinalize.
тире

38

SupressFinalizeповідомляє системі, що яка б робота не була виконана у фіналізаторі, вже зроблена, тому фіналізатор не потрібно викликати. З Документів .NET:

Об'єкти, що реалізують інтерфейс IDisposable, можуть викликати цей метод із IDisposable.Розпоряджуйте метод, щоб запобігти виклику сміттєзбірника Object.Finalize на об'єкт, який цього не вимагає.

Загалом, більшість будь-яких Dispose()методів повинні бути спроможні викликати GC.SupressFinalize(), оскільки він повинен очистити все, що було б очищено у фіналізаторі.

SupressFinalizeце лише щось, що забезпечує оптимізацію, яка дозволяє системі не турбувати чергування об’єкта до потоку фіналізатора. Правильно написаний Dispose()/ фіналізатор повинен працювати належним чином із закликом або без нього GC.SupressFinalize().


2

Цей метод повинен бути викликаний Disposeметодом об'єктів, що реалізує IDisposable, таким чином, GC не викликає фіналізатор в інший раз, якщо хтось викликає Disposeметод.

Дивіться: Метод GC.SuppressFinalize (Object) - Microsoft Docs


9
Я думаю, що "Повинен" помилятися - навіть не повинен "- Просто в деяких сценаріях ви можете усунути накладні очікування / доопрацювання об'єкта.
Основна

1
Dispose(true);
GC.SuppressFinalize(this);

Якщо об'єкт має фіналізатор, .net ставить посилання у чергу черпання.

Оскільки у нас є дзвінок Dispose(ture), це чіткий об’єкт, тому для цієї роботи нам не потрібна черга завершення.

Тому виклик GC.SuppressFinalize(this)видалення посилання у черзі на завершення.


0

Якщо клас або що-небудь, що виходить з нього, може містити останню пряму посилання на об'єкт із фіналізатором, то GC.SuppressFinalize(this)або GC.KeepAlive(this)повинен бути викликаний на об'єкт після будь-якої операції, на яку цей фіналізатор може негативно вплинути, забезпечуючи тим самим, що фіналізатор виграє Не запускатись до завершення операції.

Вартість GC.KeepAlive()і GC.SuppressFinalize(this)по суті однакова в будь-якому класі, який не має фіналізатора, і класи, у яких є фіналізатори, зазвичай повинні викликати GC.SuppressFinalize(this), тому використання останньої функції в якості останнього кроку Dispose()може не завжди бути необхідним, але це не буде помилятися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.