Утечки памяти довольно редко, но рано или поздно появляются в профессиональной деятельности разработчика ПО. Поиск причины утечки всегда непрост, особенно в больших проектах. Приведу пример, как я искал такую утечку, возможно это поможет тем, кто столкнулся с подобным.
Что мы имеем
Есть проект, который я первый раз вижу, система учета проезда машин через КПП. Стек средств и технологий: Tomcat 8, Java 1.8, GWT 2.5, Hibernate 3.6.7, Maven. При определенных настройках фильтрации пунктов пропуска использование памяти растет до потолка и портал перестает работать.
С чего начать
Почему то вначале заказчик намекал, что дело в устаревшем Tomcat. Пробовал обновлять Tomcat, Hibernate, но нет, это путь на удачу и скорее бесполезный. Правильно начинать с диагностики и анализа, и хорошо, что средства для этого есть. Самое простое — это Java VisualVM, которое идет в комплекте с Java.
Java VisualVM
Запускаем, проделываем махинации в ПО, после которых начинает расти память. Результат на рисунке:
Видно, что память занимают строки и много занимают объекты Hibernate, что наводит на мысль на взаимосвязь. Можно сделать snapshot и посмотреть классы, но делать это в Java VisualVM очень не удобно, он тормозит, плохо структурирует. Поискав удобное средство для анализа дампа памяти, выбор пал на Mat (Eclipse Memory Analyzer), потому что бесплатный.
Mat (Eclipse Memory Analyzer)
В Mat необходимо открыть дамп памяти далее перейти к Dominator Tree.
Dominator Tree отображает содержимое дампа памяти в виде дерева java-объектов, сразу упорядочивает от большего к меньшему. Видим, что память заняли несколько одинаковых по размеру потоков. Раскрыв в дереве содержимое одного из потоков видна вся суть проблемы. Мы видим SQL-запрос размером 16 Мб. Получается где-то неправильно формируется запрос с помощью Hibernate, остается найти это место и исправить. Проанализировав текст запроса, найти по коду место его формирования не составляет труда.