JavaRush /Java блог /Java Developer /Ще про збирача сміття в Java
Автор
Milan Vucic
Репетитор по программированию в Codementor.io

Ще про збирача сміття в Java

Стаття з групи Java Developer
Привіт! У минулій лекції ми вперше познайомилися з вбудованим механізмом мови Java – збирачем сміття. Він функціонує у фоновому режимі під час роботи твоєї програми, збирає об'єкти, які стали непотрібними і які надалі буде видалено. Таким чином, він звільняє пам'ять для створення нових об'єктів у майбутньому. У цій лекції ми докладніше розберемо принцип його роботи. Наприклад, як і в який момент об'єкт стає непотрібним? І як про це дізнається збирач сміття? На ці запитання і відповімо :) Лекція в нас радше оглядова: цей матеріал не треба заучувати напам'ять. Він покликаний скоріше розширити твій кругозір стосовно роботи пам'яті та збирача сміття, тому буде достатньо прочитати його і винести щось нове для себе :) Поїхали! Перше, про що тобі треба пам'ятати – збирач сміття працює паралельно з твоєю програмою. Він не є її частиною і функціонує окремо: для опису цього ми в минулій лекції наводили аналогію з роботом-пилосмоком. Насправді, так було не завжди. Раніше збирач сміття був влаштований таким чином, що працював в одному потоці з твоєю програмою. І за якимось графіком, раз на скількись хвилин, починав перевірку на наявність у програмі непотрібних об'єктів. Проблема полягала в тому, що на час цієї перевірки та збирання сміття програма зависала і не виконувалася. Уяви, що ти сидиш в офісі за роботою. Але тут приходить прибиральниця, якій потрібно вимити підлогу в приміщенні. Вона виганяє тебе з-за комп'ютера на 5 хвилин і ти чекаєш, поки вона не закінчить прибирання. У цей час ти працювати не можеш. Ось приблизно так раніше працювали збирачі сміття :) Пізніше цей механізм змінили, і тепер збирач сміття працює у фоновому режимі, не гальмуючи роботу самої програми. Ти вже знаєш, що об'єкт помирає, коли на нього не залишається жодного посилання. Але насправді збирач сміття не рахує посилання на об'єкти. По-перше, це може бути досить довго. По-друге, не дуже ефективно. Адже об'єкти можуть посилатися один на одного! Ще про збирача сміття в Java - 1На малюнку зображено приклад, коли 3 об'єкти посилаються один на одного, але більше на них не посилається ніхто. Тобто для роботи іншої частини програми вони не потрібні. Якби збирач сміття займався просто підрахунком посилань, усі ці 3 об'єкти залишилися і не звільнили б пам'ять: посилання на них є! Це можна порівняти з космічним кораблем. Космонавти під час польоту вирішили перевірити список запчастин для ремонту і виявили серед них кермо і педалі від звичайного автомобіля. Вони явно тут не потрібні і займають зайве місце. Хоч ці деталі пов'язані і мають якісь функції, в межах роботи космічного корабля це непотрібне сміття, якого краще позбутися. Тому в Java ухвалили рішення зробити основою для збирання сміття не підрахунок посилань, а поділ об'єктів на два види – досяжні та недосяжні. Як визначити, чи є об'єкт досяжним? Усе геніальне просто. Об'єкт вважається досяжним, якщо на нього посилається інший досяжний об'єкт. Виходить такий "ланцюжок досяжності". Він починається під час запуску програми і тягнеться протягом усього часу її роботи. Це виглядає приблизно ось так: Ще про збирача сміття в Java - 2Стрілка на малюнку позначає виконуваний код нашої програми. У коді, наприклад, у методі main(), створюються посилання на об'єкти. Ці об'єкти можуть посилатися на нові об'єкти, ті – ще на якісь і так далі. Утворюється ланцюжок посилань об'єктів. Якщо від об'єкта цим ланцюжком посилань можна дістатися "кореневого посилання", тобто до того, яке безпосередньо створюється у виконуваному коді, – він вважається досяжним. У нас на малюнку вони позначені синім кольором. А ось якщо об'єкт випав із цього ланцюжка, тобто жодна зі змінних у коді, що виконується на цей момент, не містить посилань на нього, і за "ланцюжком посилань" до нього теж неможливо дістатися – він вважається недосяжним. У нашій програмі два таких об'єкти позначені червоним кольором. Зверни увагу: у цих "червоних" об'єктів є посилання один на одного. Але, як ми і казали раніше, сучасний збирач сміття в Java не займається підрахунком посилань. Він визначає досяжність або недосяжність об'єкта. Тому два червоні об'єкти на малюнку стануть його здобиччю. Тепер розглянемо весь процес від початку до кінця, а заразом подивимося, як влаштована пам'ять у Java :) Усі об'єкти в Java зберігаються в спеціальній ділянці пам'яті, яка називається купа (heap). У звичайній мові "купою" називають гору предметів, де все підряд валяється упереміш. Але купа в Java не така. Вона має дуже логічну і розумну структуру. Одного чудового дня програмісти Java виявили, що всі об'єкти в їхніх програмах можна розділити на два типи – умовно кажучи, прості об'єкти і "довгожителі". "Довгожителями" вважаються об'єкти, які пережили багато збірок сміття. Найчастіше вони будуть існувати до кінця роботи програми. У підсумку загальна купа, де зберігаються всі створені об'єкти, була розділена на кілька частин. Перша частина має красиву назву – Eden (бібл. "Райський сад"). Це чудова назва, адже саме сюди об'єкти потрапляють після їхнього створення. Саме в цій частині виділяється пам'ять для нових об'єктів, коли ми пишемо new. Об'єктів може бути створено багато, і коли в цій ділянці закінчується місце, починається перша, "швидка" збірка сміття. Треба відзначити, що збирач сміття дуже розумний і обирає алгоритм роботи залежно від того, чого в купі більше – сміття чи робочих об'єктів. Якщо майже всі об'єкти є сміттям, збирач позначає "живі" об'єкти і переносить їх в іншу ділянку пам'яті, після чого поточну ділянку очищують повністю. Якщо ж сміття мало і більшу частину займають живі об'єкти, він позначає сміття, очищає його, а решту об'єктів компонує. Ми сказали "збирач позначає "живі" об'єкти і переносить їх в іншу ділянку пам'яті", але в яку? Ділянка пам'яті, куди переносяться всі об'єкти, які пережили хоча б одну збірку сміття, називається Survival Space ("місце для тих, хто вижив"). Survival Space зі свого боку поділяється на покоління. Кожен об'єкт належить до свого покоління залежно від того, скільки збірок сміття він пережив. Якщо одну – він належить до "Покоління 1", якщо 5 – до "Покоління 5". Разом Eden і Survival Space утворюють ділянку під назвою Young Generation ("молоде покоління"). Крім Young Generation у купі існує й інша ділянка пам'яті – Old Generation ("старе покоління"). Сюди якраз потрапляють ті самі об'єкти-довгожителі, які пережили багато збірок сміття. Їх вигідніше зберігати окремо від усіх інших. І тільки коли ділянка Old Generation заповнена, тобто навіть об'єктів-довгожителів у програмі так багато, що пам'яті не вистачає, здійснюється повне прибирання сміття. Вона обробляє не одну ділянку пам'яті, а взагалі всі створені Java-машиною об'єкти. Природно, вона забирає значно більше часу і ресурсів. Саме тому об'єкти-довгожителі було вирішено зберігати окремо. Коли місце закінчується в інших ділянках, здійснюється так зване "швидке збирання сміття". Воно охоплює тільки одну ділянку, і завдяки цьому є більш економічною і швидкою. Наприкінці, коли забита вже навіть ділянка для довгожителів, у бій вступає повне прибирання. Таким чином, найбільш "великоваговий" інструмент використовується збирачем тільки тоді, коли без цього вже не обійтися. Схематично структура купи і прибирання виглядають так: Ще про збирача сміття в Java - 3
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ