Диагностика утечек памяти в Java

    В данной заметке я хочу показать каким образом можно определять и устранять утечки памяти в Java на примере из моей повседневной работы. Мы не будем здесь рассматривать возможные причины появления утечек, об этом будет отдельная статья, так как тема достаточно обширная. Стоит заметить, что речь пойдет о диагностике именно Heap Memory, об утечках в других областях памяти будет отдельная статья.

    Инструменты


    Для успешной диагностики нам понадобятся два инструмента: Java Mission Control (jmc) и Eclipse Memory Analyzer. Вобщем-то можно обойтись только Memory Analyzer, но с JMC картина будет более полной.

    • JMC входит в состав JDK (начиная с 1.7)
    • Memory Analyzer может быть загружен отсюда: MAT

    Анализ использования памяти


    Прежде всего, нужно запустить приложение со следующими флагами JVM:
    -XX:+UnlockCommercialFeatures
    -XX:+FlightRecorder


    Не используйте эти опции на production системе без приобретения специальной лицензии Oracle!

    Эти опции позволят запустить Flight Recorder – утилита, которая поможет собрать информацию об использовании памяти (и много другой важной информации) во время выполнения программы. Я не буду описывать здесь как запустить Flight Recorder, эта информация легко гуглится. В моем случае было достаточно запустить FR на 10-11 минут.

    Рассмотрим следующий рисунок, на котором показана классическая «пила» памяти, а так же важный сигнал, что что-то не так с использованием памяти:

    Запись Fight recorder

    Можно увидеть, что после каждого цикла очистки памяти, heap все больше заполняется, я выделил это желтым треугольником. «Пила» все время как бы ползет вверх. Это значит, что какие-то объекты не достижимы для очистки и накапливаются в old space, что со временем приведет к переполнению этой области памяти.

    Выявление утечки


    Следующим шагом нужно выявить, что именно не доступно для очистки и в этом нам поможет Memory Analyzer. Прежде всего, нужно загрузить в программу heap dump работающего приложения с предполагаемой утечкой памяти. Это можно сделать с помощью «File → Acquire Heap Dump». После загрузки в диалоге «Getting Started Wizard» выбрать «Leak Suspects Report» после этого откроется краткий обзор возможных утечек памяти:

    Leak suspects report

    Если вернуться на вкладку «Overview» и выбрать «Dominator Tree», то можно увидеть более подробную картину:

    Overview

    Denominator tree

    Дерево показывает структуру «тяжелого» объекта, а так же размер его полей (по типу). Можно видеть, что одно из полей объекта MasterTenant занимает более 45% памяти.

    Устранение утечки


    Имея результат анализа из предыдущего пункта, следующим шагом идет устранение накапливания объектом памяти. Тут все сильно зависит от конкретного кода. Общая рекоменация – нужно найти и проанализировать все места, где происходит инициализация или изменение соответствующего поля или полей, чтобы понять механизм накапливания памяти. В моем случае в коллекцию постоянно добавлялись записи из множества (около 150) потоков при определенных условиях.

    После находжения и устранения утечки, не лишним будет пройти все шаги снова, проанализировать память и отчет Memory Analyzer, чтобы убедиться что фикс помог.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 22

      +5
      Прежде всего, нужно запустить приложение со следующими флагами JVM:
      -XX:+UnlockCommercialFeatures
      -XX:+FlightRecorder

      Эти опции позволят запустить Flight Recorder – утилита, которая поможет собрать информацию об использовании памяти (и много другой важной информации) во время выполнения программы. Я не буду описывать здесь как запустить Flight Recorder, эта информация легко гуглится. В моем случае было достаточно запустить FR на 10-11 минут.

      К этому всегда полезно добавлять дисклеймер, о том что эти фичи нельзя использовать в production-окружении не имея коммерческой лицензии на Oracle JDK. Для разработки/отладки JMC использовать можно спокойно.

        0
        Спасибо за замечание, добавил предупрежение
        +1

        Ещё полезная ссылка: http://blogs.atlassian.com/2013/03/so-you-want-your-jvms-heap/


        Описано как снимать дапм памяти максимально быстро (в случае если надо снять дамп на проде и нужно минимизировать время простоя). Фактически, при таком способе, скорость создания дампа ограничена только скоростью дисковой подсистемы, что на нормальных серверах весьма приятно.


        Результат спокойно открывается в современной версии MAT без предварительных преобразований. Главное чтобы версия JVM совпадала с версией с которой дамп снимался.

          0
          Спасибо, изучу материал по ссылке
            0
            Затверждаю. Очень полезная метода.
            Только так удалось снять дамп с живого прода.
              0

              К стати, если делать дамп таким макаром, то там ещё и информация о потоках сохраняется, Это позволяет сразу переходить к списку потоков (большая жёлтая шестерёнка на скриншоте MAT) и смотреть какой поток сколько памяти удерживает и почему. Сокращает время поиска причины утечки памяти буквально до нескольких минут.


              Буквально вот только что очередной дамп расковырял. Основное время ушло на перекачку дампа к себе на комп и его индексирование. Далее буквально за пару минут виноватого нашёл.

              0
              При использовании Commercial Features надо не забыть купить лицензию на Java SE Advanced Desktop (40$) или Java SE Advanced (5000$) или Java SE Suite (15000$).
                0

                Как я говорил выше, для разработки (не в production) можно использовать JRF не имея коммерческой лицензии:


                The Java Flight Recorder (JFR) is a commercial feature. You can use it for free on developer desktops/laptops, and for evaluation purposes in test, development, and production environments. However, to enable JFR on a production server, you require a commercial license. Using JMC UI for other purposes on the JDK does not require a commercial license.

                https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr002.html

                0
                Не думаю, что в этом контексте вообще нужно упоминать про коммерческие фичи. Все это видно и через JMX в том же VisualVM.
                  +1

                  Как только вы набрали -XX:+UnlockCommercialFeatures, нужно говорить про коммерческие фичи, если вы сделали это на проде (если говорить в контексте JMC/JFR). Может, конечно, факт неиспользования включенной фичи уменьшит штраф в судебном решении ,)

                  +3
                  Статья в стиле «чтобы нарисовать кошку надо нарисовать кружок, потом треугольники ушей, несколько сосисок- лап, а потом дорировать кошку».

                  Чтобы искать утечку памяти надо глубоко понимать программу, в которой утечки ищутся. Чтобы заметить странность. А как её видеть- объяснить сложно. Временами методика поиска напоминает анекдот про апельсин.
                    0
                    Действительно, в каждой программе могут быть свои особенности утечки памяти, статья показывает лишь общие принципы поиска утечек, которые будут одинаковы для всех программ. Естественно, это не единственный способ, всего лишь один из
                    0
                    А принципиальные отличия/преимущества от VisualVM? Какое-то сравнение с VisualVM можете сделать?
                      0
                      Например такое принципиальное отличие как возможность сделать запись работы VM и потом подробно изучать его. В Visual VM можно смотреть только в реалтайме.
                        0
                        Если речь про JMC — то ещё возможность семплирования (но работает только в safe-points, а это недостоверно).
                        Если про EMT — то GC-root как-то лучше работает (в visualVM оно то ли не работает, то ли неработоспособно). Но памяти этот зверь кушает- очень хорошо.

                        PS: apangin вроде как делал своё семплирование, которое работает вне safe-point — но всё ссылку на утилитку не могу найти. Подскажите, кто помнит, пожалуйста :)
                          0
                          Да, только это для профилирования CPU, а не Memory.
                          https://github.com/apangin/async-profiler

                          Кстати говоря, на предстоящем JPoint будем с коллегой подробно рассказывать как раз про методики семлирования, и к этому событию приурочу кое-какие интересные обновления в async-profiler… Следите за новостями :)
                        0
                        Сказали о JMC и MAT, а в чем главное отличие не сказали. Для меня главное отличие в том что MAT подходит только для статического анализа дампа. С JMC можно увидеть динамику расхода памяти по методам и к тому же по потокам пока программа еще работает. Это функция бывает незаменима.
                          –1
                          Спасибо за тему!!!!!
                            +2
                            > «Пила» все время как бы ползет вверх. Это значит, что какие-то объекты не достижимы для очистки и накапливаются в old space, что со временем приведет к переполнению этой области памяти.

                            Плохая иллюстрация, мы просто не увидели full GC — который собрал бы мусор, случайно попадающий в old space. Соответственно на хорошей иллюстрации должно быть более одного цикла такого full gc для понимания его динамики.

                            И, кстати, зачем надо было частично(!) неодинаково(!) замазывать одинаковые(!) имена пакетов на скриншоте? они же легко мержатся и начинают читаться: image
                              0
                              мы просто не увидели full GC — который собрал бы мусор, случайно попадающий в old space


                              Случайно? В янг спейс не влезло — это сегодня называется «случайно»? При условии, что в дефолтных настройках хип делится в соотношении 1 к 2 (1/3 для янг спейса, 2/3 для олд)
                              –1
                              Кстати, в NetBeans есть встроенный профайлер, который позволит сделать (даже в динамике) тоже самое не покидая родную IDE.
                                0
                                Да, это вроде VisualVM.
                                Только жаль, что в Java6 не работает.

                              Only users with full accounts can post comments. Log in, please.