Как работает сборщик мусора (Garbage Collector) в Dart?

«Как работает сборщик мусора (Garbage Collector) в Dart?» — вопрос из категории Dart Core, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Dart использует продвинутый сборщик мусора (GC), который автоматически управляет памятью. Вот как я с этим работаю в Flutter-приложениях:

Как работает GC в Dart:

class MemoryManagementExample {
  // Пример управления памятью в Dart
  void demonstrateGarbageCollection() {
    // Создаем объекты
    var heavyObject1 = _createHeavyObject();
    var heavyObject2 = _createHeavyObject();

    print('Objects created');

    // heavyObject1 становится недостижимым после этой строки
    // GC может собрать его в следующем цикле
    heavyObject1 = heavyObject2;

    // Явно помогаем GC, обнуляя ссылки
    heavyObject2 = null;

    // Вызываем сборку мусора (в основном для тестирования)
    // В продакшене это не нужно делать вручную
    // import 'dart:developer';
    // debugger();
  }

  List<String> _createHeavyObject() {
    return List.generate(10000, (index) => 'Item $index');
  }
}

// Практический пример с StreamController, где важно избегать утечек
class DataBloc {
  StreamController<String> _dataController = StreamController.broadcast();
  List<StreamSubscription> _subscriptions = [];

  void initialize() {
    // Подписываемся на поток
    final subscription = _dataController.stream.listen((data) {
      print('Received: $data');
    });

    _subscriptions.add(subscription);
  }

  void dispose() {
    // ВАЖНО: закрываем контроллер и отписываемся
    // Без этого GC не сможет собрать объекты
    for (var subscription in _subscriptions) {
      subscription.cancel();
    }
    _subscriptions.clear();

    _dataController.close();
    _dataController = null!;

    print('Resources disposed, ready for GC');
  }
}

Типы сборщиков мусора в Dart:

  1. Generational GC (по умолчанию):

    • Разделяет объекты на "молодые" и "старые"
    • Чаще сканирует молодые объекты
    • Меньшие паузы (stop-the-world)
  2. Compacting GC:

    • Устраняет фрагментацию памяти
    • Перемещает объекты в памяти
    • Используется для больших хипов

Особенности в Flutter:

class ImageCacheManagement {
  // Flutter имеет собственные кэши, которые нужно чистить
  void clearImageCache() {
    // Очищаем кэш изображений при нехватке памяти
    imageCache.clear();
    imageCache.clearLiveImages();
  }

  // Использование WeakReference для избежания утечек
  void useWeakReferences() {
    final strongRef = SomeObject();

    // WeakReference позволяет GC собрать объект,
    // даже если на него есть weak-ссылка
    final weakRef = WeakReference<SomeObject>(strongRef);

    // Проверяем, жив ли еще объект
    if (weakRef.target != null) {
      print('Object is still alive');
    }
  }
}

Лучшие практики для работы с GC:

  1. Закрывайте ресурсы: StreamController.close(), Timer.cancel()
  2. Избегайте циклических ссылок через WeakReference
  3. Используйте const для создания иммутабельных объектов
  4. Очищайте большие коллекции, когда они не нужны
  5. Для тяжелых вычислений используйте Isolate, чтобы не блокировать основной поток

Мониторинг памяти в Flutter DevTools:

  • Memory tab показывает использование кучи
  • Allocation tracing помогает найти утечки
  • Performance overlay показывает влияние GC на FPS