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

Какие знаешь поколения мусора?

2.0 Middle🔥 121 комментариев
#Dart#Архитектура Flutter

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

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

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

Поколения мусора (Garbage Collector Generations)

Garbage Collection (GC) — это автоматический процесс освобождения неиспользуемой памяти. Большинство современных языков программирования (Java, Python, C#, Dart) используют концепцию поколений мусора для оптимизации этого процесса.

Основная идея

Гипотеза слабой смертности (Weak Generational Hypothesis):

  • Молодые объекты умирают часто и быстро
  • Старые объекты живут долго
  • Редкая ссылка молодого объекта на старый

Поэтому GC концентрируется на молодом поколении, где больше всего мусора.

Архитектура поколений (Generational GC)

Ывязь памяти:

┌─────────────────────────────────────┐
│         HEAP (куча памяти)          │
│                                     │
│  ┌──────────────┐                   │
│  │ Young Gen.   │  ← часто чистится │
│  │ (Eden + S1+S2)│  ← быстро        │
│  └──────────────┘                   │
│                                     │
│  ┌──────────────┐                   │
│  │ Old Gen.     │  ← редко чистится │
│  │              │  ← медленно       │
│  └──────────────┘                   │
│                                     │
│  ┌──────────────┐                   │
│  │ Permanent    │  ← очень редко    │
│  │ (Метаклассы) │  ← метаданные    │
│  └──────────────┘                   │
└─────────────────────────────────────┘

1. Young Generation (молодое поколение)

Молодое поколение состоит из 3 частей:

Young Generation:
┌─────────────────────────────────┐
│      Eden Space (80%)           │
│  (где создаются новые объекты)  │
├─────────────────────────────────┤
│ Survivor 1    │   Survivor 2     │
│    (S1 10%)   │      (S2 10%)    │
│  (живые объ.) │  (живые объ.)    │
└─────────────────────────────────┘

Процесс сборки мусора в Young Gen (Minor GC):

void main() {
  // 1. Создание объектов в Eden Space
  var list1 = List.filled(100, 'object'); // Eden
  var list2 = List.filled(100, 'object'); // Eden
  var list3 = List.filled(100, 'object'); // Eden

  // Eden почти полна → запускается Minor GC
  // list1 и list2 больше не используются → удалятся

  // 2. list3 остается живой → переходит в S1
  print(list3);

  // 3. Новые объекты снова в Eden
  var list4 = List.filled(100, 'object');
  var list5 = List.filled(100, 'object');

  // Eden снова полна → Minor GC
  // list4 удалится, list5 переходит в S1
  // Объекты в S1 → переходят в S2 (если пережили еще один раунд)
}

Характеристики:

  • Частота: часто (каждые 100-200ms в активном приложении)
  • Время: быстро (1-10ms обычно)
  • Размер: маленький (обычно 32-64MB)

2. Old Generation (старое поколение)

Старое поколение содержит объекты, пережившие несколько Minor GC.

class LongLivedObject {
  String name;
  List<int> data;

  LongLivedObject(this.name) {
    data = List.filled(1000, 0);
  }
}

void main() {
  // Объект создается в Young Gen
  final persistent = LongLivedObject('cache');

  // После нескольких Minor GC объект переживает пороговое количество
  // (обычно 15 сборок) → переходит в Old Gen
  // Теперь он остается там и чистится только при Major GC
}

Процесс сборки мусора в Old Gen (Major GC / Full GC):

  • Частота: редко (раз в несколько секунд или минут)
  • Время: долго (50-500ms или больше)
  • Размер: большой (обычно несколько сотен MB)
  • Impact: заметно замораживает приложение (GC pause)

3. Permanent Generation (Java) / Metaspace (Java 8+)

Эта область хранит:

  • Метаклассы Java
  • Строковые константы
  • Код методов JIT компилятора

В Dart это часть обычного heap, без отдельного поколения.

Пороги (Thresholds)

Объект переходит в следующее поколение, если:

Eden → Survivor 1: После 1 Minor GC
Survivor 1 → Survivor 2: После 2 Minor GC
Survivor → Old Gen: После tenure threshold сборок

Типичные значения:
- NewRatio: соотношение Old:Young (8:1)
- SurvivorRatio: соотношение Eden:Survivor (8:1)
- MaxTenuringThreshold: объект в Old после N сборок (15)

Типы GC алгоритмов

1. Serial GC (один поток)

// Использует один поток для сборки
// Все остановится во время GC → STW (Stop The World)
// Подходит только для очень маленьких приложений
flutter run --profile

2. Parallel GC (несколько потоков)

// Несколько потоков собирают мусор одновременно
// Быстрее Serial, но все равно STW
// Minor GC: параллельно
// Major GC: параллельно

3. Concurrent Mark Sweep (CMS)

// CMS пытается выполнять сборку одновременно с приложением
// Уменьшает паузы (STW), но медленнее в целом
// Minor GC: параллельно (STW)
// Major GC: частично параллельно (меньше STW)

4. G1GC (Garbage First) - модерн

// Самый новый и умный алгоритм
// Делит heap на регионы
// Приоритизирует регионы с наибольшим мусором
// Предсказуемые паузы (~200ms)
// Используется в современной Java и Dart

Dart GC в Flutter

// Dart использует Generational GC
// Minor GC (Scavenger):
class FlutterApp {
  void buildWidget() {
    // Эти объекты удалятся при Minor GC
    final tempList = [];
    final tempData = 'temp';
    
    return Column(
      children: [], // Minor GC удалит временные объекты
    );
  }
}

// Major GC (Mark-Sweep):
class CachedData {
  static final Map<String, dynamic> cache = {}; // Живет долго
  
  static void addData(String key, dynamic value) {
    cache[key] = value; // Переходит в Old Gen
  }
}

// Проблема: утечка памяти при неправильном кешировании
void problematicCache() {
  CachedData.addData('key1', List.filled(10000000, 0)); // Old Gen
  // Major GC не удалит, т.к. есть ссылка в static cache
  // → постепенное заполнение памяти
}

Оптимизация для Generational GC

Правило 1: Минимизировать объекты в Old Gen

// Плохо — создает много долгоживущих объектов
class BadCache {
  static List<String> allUserNames = []; // Растет бесконечно
  
  void addUserName(String name) {
    allUserNames.add(name); // Old Gen переполняется
  }
}

// Хорошо — ограничиваем размер кеша
class GoodCache {
  static final LRUCache<String, String> userNames = LRUCache(maxSize: 1000);
  
  void addUserName(String name) {
    userNames.put(name, name); // Старые удаляются автоматически
  }
}

Правило 2: Максимизировать объекты в Young Gen

// Хорошо — объекты удалятся при Minor GC
class ListBuilder {
  List<int> build() {
    final tempList = []; // Young Gen
    for (int i = 0; i < 1000; i++) {
      tempList.add(i); // Young Gen
    }
    return tempList; // Возвращаем, но временные удалятся
  }
}

Правило 3: Избегайте перемещений объектов между поколениями

// Плохо
Future<void> badApproach() async {
  final data = await fetchData(); // Young Gen
  // Ждем долго → переходит в Old Gen
  // Major GC нужна чтобы удалить
  processData(data);
}

// Хорошо
Future<void> goodApproach() async {
  final data = await fetchData(); // Young Gen
  // Сразу обрабатываем
  processData(data);
  // После функции объект удалится при Minor GC
}

Мониторинг GC в Flutter

# DevTools → Memory вкладка
# Видно:
# - Heap size
# - GC events (Minor/Major)
# - Garbage collected (сколько памяти освобождено)
# - External memory

flutter run --profile
# Откройте DevTools → Memory
# Выполняйте действия в приложении
# Смотрите график использования памяти

Вывод

Generational GC работает эффективно благодаря тому, что:

  • Minor GC часто и быстро удаляет молодые объекты
  • Major GC редко, поэтому общее время паузы минимально
  • Кеш локальности улучшается (старые объекты в одном месте)

Для Flutter разработчика важно:

  1. Минимизировать объекты, переходящие в Old Gen
  2. Максимизировать объекты, удаляемые Minor GC
  3. Избегать утечек памяти (циклические ссылки, static кеши)
  4. Мониторить Memory в DevTools
  5. Избегать больших аллокаций в горячих местах кода
Какие знаешь поколения мусора? | PrepBro