← Назад к вопросам

Как работает Garbabe Collector?

2.7 Senior🔥 91 комментариев
#Dart

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ

Garbage Collector (GC) в Dart — это фундаментальная система управления памятью, работающая прозрачно для разработчика. Это жизненно важный механизм для предотвращения утечек памяти и обеспечения стабильности приложения.

Высокоуровневый обзор

GC Dart — это трассирующий сборщик мусора (tracing garbage collector) с поколенческой стратегией:

Приложение создаёт объекты
        ↓
Объекты занимают память (heap)
        ↓
Когда память заканчивается (или по расписанию)
        ↓
GC запускается и ищет "мёртвые" объекты
        ↓
Удаляет мёртвые объекты и освобождает память
        ↓
Приложение продолжает работу

Фазы работы Garbage Collector

1. Mark (маркирование) — РАЗМЕТКА

GC определяет, какие объекты ещё нужны приложению:

Где начинать поиск? — КОРНИ (roots)
├── Глобальные переменные
├── Переменные на стеке вызовов
├── Регистры CPU
└── Статические переменные

Из каждого корня GC идёт по ссылкам:
root → object1 → object2 → object3
                      ↓
                   ЖИВОЙ (marked)

Пример:

void main() {
  var list = [1, 2, 3];        // GC видит: глобальная ссылка
  var user = User(name: "John"); // GC видит: еще одна ссылка
  
  function();
  
  // После выхода из function() все локальные переменные удалены
  // GC заметит это при следующем запуске
}

void function() {
  var temp = <String>[]; // GC видит эту ссылку
  temp.add("item");
} // temp больше недостижим

2. Sweep (очистка) — УДАЛЕНИЕ

GC удаляет все объекты, которые не были помечены:

Проходим по всей памяти:
├── Object (marked)   → ОСТАВИТЬ
├── Object (не marked) → УДАЛИТЬ ✗
├── Object (marked)   → ОСТАВИТЬ
├── Object (не marked) → УДАЛИТЬ ✗
└── ...

Удалённая память возвращается в пул
и может быть переиспользована

Поколенческая сборка мусора (Generational GC)

Это ключевая оптимизация Dart — вместо проверки всей памяти GC разделяет объекты по возрасту:

Молодое поколение (Young Generation / New Space):

var tempList = []; // Новый объект
for (int i = 0; i < 1000; i++) {
  tempList.add(i);
}
// После цикла tempList удалится → GC быстро чистит молодое поколение
  • Часто содержит недолгоживущие объекты
  • Проверяется ЧАСТО (быстро)
  • Маленький размер → быстрая сборка
  • Типичный размер: 2-8 MB

Старое поколение (Old Generation / Old Space):

static final cache = <String, dynamic>{};

void initializeApp() {
  cache['user_settings'] = {...}; // Долгоживущий объект
}
// cache остаётся в памяти на весь жизненный цикл приложения
  • Содержит долгоживущие объекты
  • Проверяется РЕДКО (экономит ресурсы)
  • Больший размер
  • Если объект пережил несколько GC молодого поколения → переходит в старое

Диаграмма жизненного цикла объекта

Объект создан
        ↓
   Young Space  ← GC проверяет часто
        ↓
  Пережил GC?
   Yes ↙     ↘ No
    ↓         (удалить)
  ...        
        ↓
  Пережил N сборок мусора?
   Yes ↙     ↘ No
    ↓         (удалить)
   Old Space  ← GC проверяет редко
        ↓
  Ещё нужен?
   Yes ↙     ↘ No
    ↓         (удалить)
   ...

Алгоритм Scavenge (для Young Space)

Молодое поколение использует специальный алгоритм:

1. Скопировать живые объекты из Young → Survivor space
2. Удалить всё остальное
3. Survivor space становится новым Young space
4. Старый Young space становится новым Survivor

Это быстрее, чем Mark-and-Sweep!

Когда запускается GC

GC не запускается случайно — это событие, вызванное определёнными условиями:

// 1. Когда памяти недостаточно
var bigList = List.filled(1000000, "x");
// "Стоп, памяти нет!" → GC запускается

// 2. По расписанию (если задано)
// Dart может проверить gc() каждые N миллисекунд

// 3. Явно вызвать (редко)
import 'dart:developer';
service.requestGC(); // Попросить GC

// 4. Выход из области видимости
void function() {
  var tempData = heavyComputation();
  processData(tempData);
} // Здесь tempData может быть удалена → GC кандидат

Важные концепции

Достижимость (Reachability):

var root = User(id: 1);
var temp = root; // Две ссылки на один объект
root = null;     // Один путь блокирован
// temp всё ещё существует!
temp = null;     // Теперь объект недостижим → можно удалить

Циклические ссылки:

class Node {
  Node? next;
}

var node1 = Node();
var node2 = Node();
node1.next = node2;
node2.next = node1; // Цикл!

node1 = null;
node2 = null;
// GC видит, что ОБА объекта недостижимы из root
// и удаляет оба → нет утечки памяти

Слабые ссылки (Weak References):

import 'dart:core';

var object = ExpensiveObject();
var weakRef = WeakReference(object);

object = null; // Объект может быть удалён GC
var retrieved = weakRef.target; // Может вернуть null!

// Используется в кешах, обсерверах и т.д.

Практические советы

Избегайте утечек памяти:

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription _subscription;

  @override
  void initState() {
    super.initState();
    _subscription = stream.listen((data) {
      setState(() => this.data = data);
    });
  }

  @override
  void dispose() {
    _subscription.cancel(); // Критично! Иначе утечка памяти
    super.dispose();
  }
}

Профилирование памяти:

import 'dart:developer';

void profileMemory() async {
  // Запросить информацию о памяти
  final info = await service.requestVMTimelineInfo();
  print('Memory usage: ${info}');
}

Перфоманс заметки

  • Young space GC: ~10-50ms (обычно не заметно)
  • Old space GC: ~100-500ms (может вызвать "фриз")
  • Частые GC → потеря производительности
  • Редкие GC → растет потребление памяти

GC Dart автоматически балансирует эти параметры.

Понимание работы GC помогает писать эффективный код и избегать утечек памяти.

Как работает Garbabe Collector? | PrepBro