Финализация и Disposable

Я помогаю Golang, Java и C# разработчикам находить работу и проходить собеседования: резюме и отклики, которые доходят до HR, спокойный собес и торг по офферу. Разбираю механику найма изнутри, я провёл более 100 собеседований. Без воды, только чек-листы и готовые фразы.

финализацияdisposableidisposable

Вопросы о финализации и Disposable, а также ключевом слове using часто встречаются на собеседованиях. Разберемся в теме!

⚰️ Финализация

Финализация - это механизм освобождения неуправляемых ресурсов.

Неуправляемый ресурс - это ресурс, который использует неуправляемую память: файловый дескриптор, соединение с базой данных или tcp-соединение.

Объект не будет освобожден GC, если он содержит в себе член, являющийся неуправляемым ресурсом. Это приведет к утечкам памяти, потому что объекты будут оставаться несобранными.

public class MyClass
{
  // Финализатор
  ~MyClass()
  {
    // Логика по освобождению неуправляемых ресурсов
  }
}

⚙️ Как работает финализатор

Финализация выполняется после сборки мусора.

При создании объекта, реализующего финализатор, через оператор new указатель на объект помещается в список финализации (finalization list). Каждая запись из списка указывает на объект, для которой нужно вызвать метод финализации, перед освобождения занятой им памяти.

        Корни приложения 
      /                \    
  || obj1 , obj2, obj3, obj4, obj5, obj6 || - Управляемая куча
                            \        /
  Список финализации - || obj4, obj6 ||    ||   || - Очередь финализации

Сборщик мусора определяет, что объекты obj2, obj3, obj5, obj6 - это мусор: на них не ссылаются корни приложения. Выполняется поиск указателя на эти объекты в списке финализации. Найденные указатели перемещается в очередь финализации (freachable queue).

                  Корни приложения 
                  /      |              
              || obj1 , obj4, obj6 || - Управляемая куча
                        /        \
Список финализации - || obj4 ||  || obj6 || - Очередь финализации

obj2, obj3, obj5 были удалены, так как на них нет ссылок из корней приложения, и у них нет финализаторов. Память объекта obj6 освободить нельзя, так как его метод финализации еще не вызывался. После перемещения указателя из списка финализации в очередь финализации этот объект перестает считаться мусором и сборщик мусора не может его удалить. Как будто на него ссылается корень приложения.

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

При следующем проходе сборщика мусора объекты, которые были финализированы из очереди, становятся мусором. Поэтому сборщик мусора просто удаляет их.

Для сборки финализируемых объектов необходимо минимум 2 прохода сборщика мусора: первый для помещения в очередь финализации, второй для сборки финализированного объекта.

                    Корни приложения 
                    /      |              
                || obj1 , obj4 || - Управляемая куча
                          /        
Список финализации - || obj4 ||  ||  || - Очередь финализации

🧹 Паттерн Disposable

Паттерн Disposable служит той же цели, что и финализатор. Но позволяет управлять моментом, когда неуправляемые ресурсы будут освобождены. Для его реализации существует интерфейс IDIsposable и его асинхронный аналог IAsyncDisposable.

В реализации метода Dispose должна быть реализована логика освобождения неуправляемых ресурсов.

Рекомендуется использовать комбинированный подход с финализатором на случай, если разработчик забудет вызывать Dispose.

Следует работать с Disposable объектами с помощью ключевого слова using.

Это синтаксический сахар для упрощения освобождения ресурсов. Он разворачивается в блок try-finally, который всегда вызовет метод Dispose после выполнения логики.

using(var disposableObject = new DisposableObject())
{
    // Какая-то логика
}

🎯 Вывод

Теперь ты знаешь, как работает очистка неуправляемых ресурсов!

#техничка

Читайте так же