В этой статье поговорим о сборке мусора (Garbage Collection) в java

Память в JVM делится на три части:

  • Young generation (молодое поколение).

  • Old generation (старое поколение).

  • Metaspace (Perm Gen).

Young Generation (молодое поколение)

Как следует из названия, Young Generation — это область памяти для новых, вновь создаваемых объектов.

  • Когда область Young Generation заполняется, то запускается минорная сборка мусора (Minor GC).

  • При Minor GC "мертвые" объекты удаляются из Young Generation.

  • Чем больше "мертвых" объектов в Young Generation, тем быстрее выполняется Minor GC.

  • При Minor GC происходит "остановка мира" (stop the world) — все потоки в  приложении останавливаются.

Давайте подробнее разберемся со структурой Young generation.

Young generation разделен на три части: Eden, S0, S1.

  • Все новые  объекты размещаются в Eden Space.

  • При заполнении Еden space происходит minor GC: все "живые" объекты перемещаются в одно из пространств уцелевших объектов (survivor space): S0 или S1. Допустим, в нашем случае, все объекты будут перемещены в S0.

Для дальнейшего эксперимента я написал и запустил программу, которая создает короткоживущие объекты.

Давайте посмотрим распределение памяти в Visual GC (плагин для VisualVM).

Как видно, в S0 совсем мало объектов, и как только Еden Space заполняется, все объекты, на которые есть ссылки, перемещаются в S1.

Old generation (старое поколение)

  • В Old generation располагаются долгоживущие объекты.

  • Как правило, эта область больше, чем область для young generation.

  • При заполнении (или достижении заданного порога) происходит Major GC.

  • Обычно Major GC выполняются медленнее и реже, чем minor GC.

Как с помощью этой информации можно оптимизировать использование памяти?

Все зависит от типа приложения.

Если у вас много временных объектов, то будет много Minor GC. В этом случае можно использовать параметр XX:NewRatio=1, чтобы распределить 50% на young generation и 50% на old generation.

По умолчанию NewRatio=2, т.е. young generation составляет 1/3 всей кучи (heap).

При множестве долгоживущих объектов можно сделать область старого поколения больше, увеличив NewRatio.

Зачем используется два survivor space?

Вы наверняка задумались над тем, почему используется два survivor space? Для борьбы с фрагментацией памяти. При каждом копировании объектов из eden в survivor, вы получаете пустой eden и один пустой survivor.

Алгоритмы сборки мусора

В JVM есть несколько сборщиков мусора с разными алгоритмами для молодого и старого поколения. Есть три типа алгоритмов.

Serial collector (последовательный сборщик)

Для сборки мусора используется один поток. Подходит для простых приложений с однопроцессорными машинами.

Parallel collector (параллельный сборщик)

В отличие от последовательного сборщика, использующего один поток, параллельный использует несколько потоков. Эффективен при достаточном объеме памяти и количестве процессорных ядер.

Concurrent collector (конкурентный сборщик)

Concurrent collector выполняет сборку мусора вместе с работой потоков вашего приложения. Эффективен для приложений, обрабатывающих средние и большие наборы данных и требующих малого времени отклика.

Для молодого и старого поколений можно использовать разные, но совместимые, алгоритмы GC. Например, нельзя использовать Parallel Scavenge для молодого поколения одновременно с Concurrent Mark Sweep для старого, так как Parallel Scavenge не обеспечивает синхронизацию, которая требуется в CMS.

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


Приглашаем всех желающих на demo-урок «Переопределение, скрытие, передекларация». На занятии рассмотрим переопределение и скрытие методов в Java, а также передекларация и скрытие переменных; познакомимся с четырьмя правилами, а потом ещё и с пятым. Регистрация доступна по ссылке.

Комментарии (14)


  1. CrushBy
    30.03.2022 15:41
    +4

    Java 11 вышла в 2018 году. Там default GC - G1. В 2021 году вышла Java 17, где уже в production перешел ZGC (у которого очень низкие паузы), а также улучшен алгоритм Shenandoah (который тоже в 11 появился, и тоже с низкими паузами).

    Так что, в любом случае, описанные выше алгоритмы немножко устарели, скажем прямо.


    1. GaDzik
      30.03.2022 16:16
      -3

      Простите. А вы можете запилить статью на эту тему?


    1. sshikov
      30.03.2022 23:01

      Ну, Java 8 вышла в 2014 но все еще активно используется, и последняя сборка 321 вышла совсем недавно, в феврале вроде. Есть такие места, где не переводятся на новую версию раз в полгода, и никогда не будут, скорее всего.

      Хотя конечно, очередная статья про нее через 8 лет после выхода — глупость несусветная.


      1. CrushBy
        31.03.2022 10:03

        Да, у нас тоже еще местами используется Java 8. Но без G1 сборщика мусора на большой нагрузке (где больше 64ГБ памяти) скажем прямо не очень хорошо. Пи высокой скорости генерации мусора попасть из-за дефрагментации памяти в STW в Java 8 очень просто. И главное - там будет однопоточная полная сборка мусора (что могло вешать сервак на 2 минуты при 128GB памяти). В том же G1 даже, если вдруг по ошибке пройдет полная сборка мусора, то она на том же объеме памяти занимает секунд 30.

        Но и с G1 есть проблемы, так как на памяти свыше 128ГБ Mixed сборки уже занимают больше 3 секунд, что критично (и там никакие настройки не помогают). Поэтому смотрим в сторону ZGC (даже пробовали в проде на Java 11, но там были нюансы и пришлось откатить). Там паузы были очень хорошие. В ближайшее время будем на 17й пробовать, если все ок, то будем переводить на 17ю всеъ.


        1. sshikov
          31.03.2022 18:16

          Ну, у нас все еще проще. Hadoop даже 3 версии, все еще основан на 8. Пока. Переход на 11 происходит, например Jenkins, и т.п. куски CI/CD. То есть, нам и хотелось бы, но пока все понимают, что это не маленькая работа. Мы на 3 хадуп-то уже второй год переходим. Там большая экосистема, нам только совместить все это с миграцией на новую JDK не хватает для полного счастья :)


  1. rdo
    30.03.2022 15:53
    +3

    Извините, насколько актуальна эта статья? Я похожие уже лет пять-семь читаю, хотелось бы чего-нибудь актуального.


    1. GaDzik
      30.03.2022 16:15
      -2

      Мне для повторения очень даже норм. Вот сейчас только я очевидно запомнил как именно работает GC и что можно там настроить.


      1. Akon32
        30.03.2022 16:22
        +2

        Что толку повторять, если сейчас используются уже другие gc? В статье информация ~десятилетней давности, если не двенадцатилетней.

        И действительно, ссылки ведут в статьи из 2011 года.


        1. GaDzik
          30.03.2022 16:26

          Для меня явно есть толк. Уже то что вы мне сейчас пытаетесь сказать тоже польза)) Хоть вам это и не очень приятно.


          1. Hivemaster
            30.03.2022 19:07
            +1

            Пользуйтесь поиском, на Хабре залежи на много более полезных статей, чем этот переводной хлам. Например про сборку мусора - раз, два и три.


            1. GaDzik
              30.03.2022 19:11

              Оу на досуге прочту. Не поймите не правильно. Я просто итак много читаю.


  1. evidenzas
    30.03.2022 16:33
    +6

    Похоже что это просто повтор их же прошлогодней статьи.


    1. cypok
      30.03.2022 17:25
      +1

      О, вот это фокус.


  1. vasyakolobok77
    30.03.2022 22:21

    Статья мягко говоря "несвежая". CMS коллектор уже 2 года (с 14 java) как вырезан.