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

время чтения: 4 мин

Cборка мусора в Java: лучшие практики

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

Автор этой статьи — Senior Software Engineer EPAM Вайбхави Дешпанде.

Читай другие статьи автора:

В первой части статьи мы сфокусировались на том, что такое сборка мусора в 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-приложениях.