Как работает сборка мусора в Dart? Какие поколения (generations) ты знаешь?

Ответ

В Dart используется поколенческий (generational) сборщик мусора (Garbage Collector, GC), оптимизированный для интерактивных приложений с коротким временем жизни большинства объектов, что типично для UI во Flutter.

Основные поколения:

  1. Молодое поколение (Young Generation / Nursery):

    • Сюда попадают все вновь созданные объекты.
    • Сборка мусора здесь происходит часто и быстро (Minor GC). Алгоритм — копирующий (copying).
    • Объекты, пережившие одну сборку, перемещаются в следующее пространство внутри молодого поколения или в старое.
  2. Старое поколение (Old Generation):

    • Здесь находятся долгоживущие объекты, пережившие несколько сборок в молодом поколении.
    • Сборка происходит реже, но может занимать больше времени (Major GC). Используется алгоритм mark-sweep-compact (пометить-очистить-уплотнить).

Как это влияет на разработку во Flutter:

  • Краткосрочные объекты (внутри build()-методов) дешевы. Они создаются в молодом поколении и быстро утилизируются.
  • Избегание утечек памяти: Важно удалять подписки (например, StreamSubscription, Listenable). Несобранные ссылки удерживают объекты в памяти, препятствуя работе GC.
  • Профилирование: В Dart DevTools можно отслеживать пики использования памяти и активность GC.

Пример, создающий нагрузку на GC:

void inefficientBuild() {
  List<String> temporaryList = [];
  for (int i = 0; i < 1000; i++) {
    temporaryList.add('Item $i'); // Создаются тысячи короткоживущих объектов
  }
  // После выхода из метода `temporaryList` и все строки становятся мусором,
  // что провоцирует частые Minor GC.
}

Лучшая практика: Использование const для виджетов и неизменяемых данных, чтобы уменьшить количество создаваемых объектов и нагрузку на GC.

Ответ 18+ 🔞

Давай разберёмся с этим вашим сборщиком мусора в Dart, а то народ пугается, как чёрт ладана. Система, надо сказать, хитрая, но в целом логичная, если вникнуть.

Представь себе, что память — это такой двухкомнатный общежитийный сортир. Первая комната — молодое поколение. Туда заходят все новенькие объекты, которые только что родились в твоём build()-методе. Там весело, шумно, но уборка (Minor GC) происходит часто и быстро, по принципу «всё, что не прибито гвоздями, — на выход». Выжившие после этой чистки переезжают в соседнюю комнату.

А соседняя комната — это уже старое поколение. Там обитают матёрые, проверенные временем объекты, которые пережили не одну уборку. Уборка здесь (Major GC) случается реже, но зато капитальная: всё перетряхивают, выкидывают хлам и компактно упаковывают. Занимает больше времени, но что поделать.

Как на это всё смотреть в контексте Flutter? Вот ты пишешь код, и у тебя каждую миллисекунду в build() рождаются и умирают сотни виджетов и строк. Это нормально! Для них эта система — просто рай. Зашёл в первую комнату, быстро пописал на стенку и вышел, а уборщик уже следом подметает. Дешёво и сердито.

Проблемы начинаются, когда ты начинаешь создавать утечки. Это как если гость из первой комнаты привяжет себя верёвкой к батарее в старой. Уборщик придёт, потрогает верёвку — ага, ссылка живая — и пойдёт дальше. А объект так и будет висеть мёртвым грузом, занимая место. Типичный случай — не отписался от StreamSubscription или AnimationController не dispose()нул. Вот это реально доверия ебать ноль к таким разработчикам.

Смотри, вот пример кода, который заставит сборщик матюгаться и потеть:

void inefficientBuild() {
  List<String> temporaryList = [];
  for (int i = 0; i < 1000; i++) {
    temporaryList.add('Item $i'); // Каждую перерисовку создаётся 1000 новых строк!
  }
  // Метод закончился, список никому не нужен. Вся эта куча объектов — мгновенный мусор.
  // GC смотрит на это и говорит: «Ёпта, опять? Да я только присел!»
}

Каждый раз, когда этот метод вызывается (а в Flutter это может быть часто), ты создаёшь овердохуища временных объектов. Молодое поколение забивается быстрее, уборки учащаются, и в какой-то момент может дёрнуться и основная, долгая уборка. А это уже просадка FPS, и пользователь думает, что приложение тормозит.

Так что делать-то? Главный совет — используй const где только можно. const Text('Привет') — это один объект на всю жизнь приложения. Он создался один раз и поселился в особом, «бессмертном» месте. Никакого мусора, никакой нагрузки на GC. Красота.

И конечно, следи за подписками и контроллерами. Закрывай за собой двери. В общем, не будь распиздяем, и сборщик мусора будет твоим тихим, почти незаметным другом, а не врагом, который постоянно орет «ублюдки, опять насрали!».