— Вітання! Знову вирішила влаштувати тобі невелику лекцію про збирання сміття.

Як ти вже знаєш, Java-машина сама відстежує ситуації, коли об'єкт стає непотрібним та видаляє його.

— Ага. Ви з Рішею раніше мені розповідали про це, нюансів я не пам'ятаю.

— Ок. Тоді повторимо.

Складання сміття - 1

Щойно об'єкт створюється, Java виділяє йому пам'ять. А за попитом об'єкта стежить за допомогою змінних-посилань. Об'єкт може бути видалений при «складання сміття» — процедурі очищення пам'яті, якщо не залишається змінних, які посилаються на цей об'єкт.

— А розкажи трохи про збирача сміття, що це таке і як він працює.

— Ок. Раніше складання сміття відбувалося в головному потоці/нитці. Раз на 5 хвилин, або частіше. Якщо наставав випадок, коли не вистачало вільної пам'яті, Java-машина припиняла роботу всіх ниток і видаляла об'єкти, що не використовуються.

Але зараз від цього підходу відмовилися. Складальник сміття нового покоління працює непомітно і в окремому потоці. Таке складання сміття прийнято називати паралельним.

— Зрозуміло. А як саме визначається – чи потрібно видаляти об'єкт чи ні.

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

Тому Java застосовує інший підхід. Java ділить об'єкти на досяжні та недосяжні. Об'єкт вважається досяжним (живим), якщо на нього посилається інший досяжний (живий) об'єкт. Досяжність вважається від ниток. Нитки, що працюють, завжди вважаються досяжними (живими), навіть якщо на них ніхто не посилається.

— Ок. З цим начебто ясно.

А як відбувається саме прибирання сміття – видалення непотрібних об'єктів?

— Тут усе просто. У Java пам'ять умовно розділена на дві частини, і коли приходить час складання сміття, всі живі (досяжні) об'єкти копіюються в іншу частину пам'яті, а стара вся звільняється.

— Цікавий підхід. І не треба рахувати посилання – скопіював усі досяжні об'єкти, а решта – сміття.

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

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

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

Складальник Сміття дуже складна і ефективна складова Java. Багато його частин працюють евристично – на основі алгоритмів-здогадок. Тому він часто «не слухається» користувача.

— У сенсі?

— Java має об'єкт GC (Garbage Collector – Складальник Сміття), який можна викликати за допомогою методу System.gc ().

Також можна примусово ініціювати виклик finalize-методів об'єктів, що видаляються, за допомогою System.runFinalization(). Але справа в тому, що за документацією Java це не гарантує ні початок складання сміття, ні виклик методів finalize(). Garbage Collector сам вирішує, що і коли йому викликати.

— Нічого собі! Знатиму.

— Але це ще не все. Як ти знаєш, Java одні об'єкти посилаються на інші, і саме за допомогою цієї мережі посилань визначається – варто видаляти об'єкт чи ні.

Так ось у Java є спеціальні посилання, які дозволяють впливати на цей процес. Для них є спеціальні класи-обгортки. Ось вони:

SoftReference – м'яке посилання.

WeakReference – слабке посилання.

PhantomReference – примарне посилання.

— М-так. Чимось нагадує внутрішні класи, вкладені класи, внутрішні анонімні класи, локальні класи. Назви різні, але за ними зовсім не зрозуміло, за що вони відповідають.

— Ось ти, Аміго, і став програмістом. Тепер ти обурюєшся щодо назв класів – мовляв, недостатньо інформативні, і не можна за однією назвою(!) визначити, що цей клас робить, як і навіщо.

— Ого. А я й сам не помітив. Але це так очевидно.

Добре. Солов'я байками не годують. Давай я тобі розповім про SoftReference – м'які посилання.

Ці посилання були спеціально придумані для кешування, хоча їх можна використовувати і для інших цілей – все на розсуд програміста.

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

Приклад
//створення об'єкта Cat
Cat cat = new Cat();

//створення м'якого посилання на об'єкт Cat
SoftReference<Cat> catRef = new SoftReference<Cat>(cat);

//Тепер на об'єкт посилається тільки м'яке посилання catRef.
cat = null;

//тепер на об'єкт посилається ще й звичайна змінна cat
cat = catRef.get();

//очищаємо м'яке посилання
catRef.clear();

Якщо на об'єкт існують тільки м'які посилання, то він продовжує жити і називається «м'яким».

Але! Об'єкт, на який посилаються тільки м'які посилання, може бути видалений збирачем сміття, якщо програмі не вистачає пам'яті. Якщо програмі раптом не вистачає пам'яті, перш ніж викинути OutOfMemoryException< /strong>, збирач сміття видаляє всі об'єкти, на які посилаються м'які посилання та спробує виділити програмі пам'ять ще раз.

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

— Ага. Мені самому сподобалося.

— Ну і маленьке доповнення: у класу SoftReference є два методи. Метод get() повертає об'єкт, на який посилається SoftReference. Якщо об'єкт був видалений збирачем сміття, раптово(!) метод get() почне віддавати null.

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

На цьому поки що все.

— Дякую за цікаву розповідь, Еллі. Справді, було дуже цікаво.