Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
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 помогает писать эффективный код и избегать утечек памяти.