1. Згадуємо, що таке складання сміття в Java

Складання сміття – це процес відновлення заповненої пам'яті середовища виконання шляхом знищення об'єктів, що не використовуються.

Іноді програміст може забути знищити непотрібні об'єкти, і виділена для них пам'ять не звільняється. Витрачається дедалі більше системної пам'яті, і зрештою вона більше виділяється. Такі програми страждають від “відпливу пам'яті”.

Після певного моменту пам'яті вже не вистачає для створення нових об'єктів, і програма завершується через OutOfMemoryError.

Складання сміття в Java – це процес, за допомогою якого програми Java автоматично керують пам'яттю. Java-програми компілюються в байт-код, який запускається на віртуальній машині Java (JVM).

Коли Java-програми виконуються на JVM, об'єкти створюються в купі, що є частиною виділеної для них пам'яті.

Поки Java-програма працює, в ній створюються нові об'єкти. Зрештою, деякі об'єкти перестають бути потрібними. Можна сказати, що будь-якої миті часу пам'ять купи складається з двох типів об'єктів.

  • Живі — ці об'єкти використовуються, на них посилаються ще звідкись.
  • Мертві ці об'єкти більше ніде не використовуються, посилань на них немає.

Збирач сміття знаходить об'єкти, які не використовуються, і видаляє їх, щоб звільнити пам'ять.

Збирання сміття в Java – автоматичний процес. Програмісту не потрібно явно подзначати об'єкти, що підлягають видаленню.

Кожна JVM може реалізувати власну версію збирання сміття. Однак збирач повинен відповідати стандартній специфікації JVM для роботи з об'єктами, що є у пам'яті купи, для маркування або ідентифікації недосяжних об'єктів та їх знищення за рахунок ущільнення.

2. Досяжність об'єктів

Щоб визнати об'єкт живим наявності посилань недостатньо. Все тому, що одні мертві об'єкти можуть посилатися на інші мертві об'єкти. Саме тому потрібно, щоб серед усіх посилань на об'єкт було хоча б одне від “живого” об'єкта.

Досяжність об'єктів

Збирачі сміття працюють з концепцією GC Roots (коренів збору сміття), щоб розрізняти живі та мертві об'єкти. Є 100% живі об'єкти, і від них йдуть посилання, які оживляють інші об'єкти тощо.

Приклади такого коріння:

  • Класи, які завантажуються системним завантажувачем класів.
  • Живі потоки.
  • Параметри методів, які нині виконуються та локальні змінні.
  • Об'єкти, які застосовуються як монітор для синхронізації.
  • Об'єкти, які утримуються зі збирання сміття для деяких цілей.
  • Збирач сміття переглядає весь граф об'єктів у пам'яті: починає з цього коріння і слідує за посиланнями на інші об'єкти.

3. Етапи збирання сміття в Java

Стандартна реалізація збирання сміття має три етапи.

1. Позначаємо об'єкти як живі

На цьому етапі збирач сміття повинен ідентифікувати всі живі об'єкти в пам'яті шляхом обходу графа об'єктів.

Коли він відвідує об'єкт, то позначає його як доступний і, отже, живий. Усі об'єкти, недоступні з кореня GC, розглядаються як кандидати на збирання сміття.

2. Зачистка мертвих об'єктів

Після фази розмітки простір пам'яті зайнятий або живими (відвіданими) або мертвими (не відвіданими) об'єктами. Фаза зачистки звільняє фрагменти пам'яті, які містять мертві об'єкти.

3. Компактне розташування об'єктів, що залишилися в пам'яті

Не обов'язково, щоб мертві об'єкти, які видалено на попередній фазі, були поруч один з одним. Таким чином, ти ризикуєш отримати фрагментований (напівпорожній) простір пам'яті.

Але, якщо передбачити це, є можливість ущільнити пам'ять у момент, коли збирач сміття видаляє мертві об'єкти. Ті, хто залишилися, будуть розташовуватися в безперервному блоці на початку купи.

Процес ущільнення полегшує послідовне виділення пам'яті нових об'єктів.