Прокачайся в код-рев’ю: для перших 50 учасників — курс безкоштовний

час читання: 4 хв

Збирання сміття в Java: найкращі практики

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

Автор цієї статті — EPAM Senior Software Engineer Вайбхаві Дешпанде.

Читай інші статті автора:

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

Тепер давайте заглибимося у дві найпоширеніші проблеми збирання сміття та розглянемо найкращі способи їх усунення з прикладами.

Дві типові проблеми збирання сміття

1. Витоки пам’яті

Витоки пам’яті відбуваються, коли об’єкти випадково залишаються в пам’яті через збережені посилання, що перешкоджають збиранню сміття. Це може призвести до надмірного використання пам’яті та можливих помилок її нестачі (англ. out-of-memory errors).

Приклад: розглянемо веб-додаток, який керує сеансами користувачів. Якщо об’єкти сеансу інвалідуються або видаляються некоректно, коли користувач розлогінюється або стає неактивним, вони можуть залишатися в пам’яті, викликаючи витік.

Метод invalidateSession()

У наведеному вище прикладі, коли користувач виходить із системи або стає неактивним, має викликатися метод invalidateSession(), щоб видалити об’єкт Session з карти сеансів, запобігаючи витоку пам’яті.

2. Проблеми продуктивності

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

Приклад: припустимо, у вас є трейдингова система, що працює в режимі реального часу, де продуктивність відіграє вирішальну роль. Якщо в збирачі сміття налаштовані параметри, які призводять до частих stop-the-world пауз, вони можуть порушити своєчасне виконання угод і вплинути на загальну продуктивність системи.

Найкращі практики збирання сміття

  • Мінімізуйте створення об’єктів

Створення непотрібних об’єктів викликає навантаження на збирач сміття. Мінімізуйте створення об’єктів, повторно використовуючи їх, а також використовуючи незмінні (англ. immutable) об’єкти, де це можливо.

Приклад: у високопродуктивному фінансовому додатку замість створення нових об’єктів BigDecimal для обчислень ви можете повторно використовувати єдиний екземпляр за допомогою методу BigDecimal.valueOf().

Метод BigDecimal.valueOf()

У наведеному прикладі метод BigDecimal.valueOf() створює єдиний екземпляр BigDecimal для кожної ітерації, скорочуючи непотрібне створення об’єктів.

  • Використовуйте локальні змінні

Використовуйте локальні змінні всередині методів замість того, щоб зберігати дані у змінних-членах. Локальні змінні мають коротший термін життя і швидко збираються в сміття.

Приклад: застосовуйте локальні змінні всередині методів, щоб уникнути непотрібного зберігання об’єктів.

Метод performCalculation()

У наведеному прикладі result є локальною змінною всередині методу performCalculation(), що гарантує її придатність для збирання сміття після завершення виконання методу.

  • Розберіться в життєвому циклі об’єктів

Оцінюйте життєвий цикл об’єктів і стежте, щоб посилання правильно прибиралися, коли об’єкти більше не потрібні. Уникайте збереження посилань на об’єкти довше, ніж це необхідно.

Приклад: переконайтеся, що застарілі об’єкти або об’єкти з минувшим терміном видаляються з кешу, щоб уникнути непотрібного зберігання.

Метод remove()

У наведеному вище прикладі метод remove() використовується для безпосереднього видалення об’єктів із кешу, коли вони більше не потрібні.

  • Налаштовуйте збирання сміття

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

Приклад: для додатків, чутливих до затримок, розгляньте використання збирача сміття з короткими паузами, наприклад, Garbage-First (G1), і налаштуйте відповідний розмір купи та параметри збирання сміття.

Розмір купи 4GB

У цьому прикладі JVM налаштована на використання збирача сміття G1 з максимальним розміром купи 4 ГБ для додатка MyApp.

Давайте подивимося, що робить наведена вище команда:

Команда java -XX:+UseG1GC -Xmx4G -Xms4G використовується для запуску Java-додатка з певними параметрами JVM, пов’язаними зі збиранням сміття і конфігурацією купи пам’яті. Розберемо значення кожного параметра:

  • -XX:+UseG1GC: активує збирач сміття Garbage-First (G1). Він забезпечує збирання сміття з малою паузою і хорошою пропускною спроможністю за рахунок поділу купи на регіони, збираючи сміття для кожного регіону окремо.
  • -Xmx4G: встановлює максимальний розмір купи, що дорівнює 4 ГБ. Максимальний розмір купи являє собою верхній поріг пам’яті, яка може бути виділена для Java-додатка. У цьому випадку для нього виділяється 4 ГБ пам’яті.
  • -Xms4G: встановлює початковий розмір купи — кількість пам’яті, виділену для Java-додатка під час запуску, — 4 ГБ. Коли це значення встановлюється рівним максимальному розміру купи (-Xmx4G), JVM виділяє весь розмір купи, заданий спочатку.

Вказавши ці параметри під час запуску Java-додатка, ви налаштовуєте JVM на використання збирача сміття G1, встановлюєте максимальний розмір купи 4 ГБ і виділяєте її загальний розмір на самому початку. Ці налаштування допомагають оптимізувати збирання сміття та виділення пам’яті для програми.

Важливо зазначити, що значення, зазначені для опцій -Xmx та -Xms, можна налаштувати відповідно до конкретних вимог і доступних системних ресурсів вашого додатка. Значення можуть бути вказані в різних одиницях, наприклад, у мегабайтах (M) або гігабайтах (G), залежно від ваших потреб.

Таким чином, команда java -XX:+UseG1GC -Xmx4G -Xms4G використовується для запуску Java-додатка з певними налаштуваннями збирання сміття та виділення пам’яті. Це дає змогу досягти оптимальної продуктивності та використання пам’яті.

Висновки

Для правильного використання пам’яті та оптимальної продуктивності в Java-додатках важливо вчасно усувати проблеми збирання сміття. Скорочення витоків пам’яті вимагає правильної інвалідації та видалення об’єктів. Щоб уникнути проблем із продуктивністю, дотримуйтесь простих рекомендацій: мінімізуйте створення об’єктів, використовуйте локальні змінні, враховуйте життєвий цикл об’єктів і коректно налаштовуйте збирача сміття. Застосовуючи ці практики, ви зведете до мінімуму поширені проблеми збирання сміття в Java-додатках.