Обнаружение утечек памяти в java приложении
Во время работы над проектом столкнулись с тем что память используемая приложением постепенно увеличивается. Приложение написано на java, но утечки памяти случаются.
Основные признаки утечки памяти:
- Серьезное снижение производительности при длительной непрерывной работе приложения
- Ошибка кучи OutOfMemoryError в приложении
- Спонтанные и странные сбои приложения
- В приложении время от времени заканчиваются объекты подключения
Методология поиска утечек памяти
Для начала необходимо определить, что в приложении есть утечка.
Первое необходимо включить логи сборки мусора для java от 1.4 до 1.8, должны добавить следующие агрументы JVM:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<file-path>
Для Java 9 и выше:
-Xlog:gc*:file=<file-path>
Есть несколько бесплатных инструментов для графического представления GC логов. Рассмотрим вывод GCViewver:

Определяем pid приложения
Это можно сделать либо с помощью стандартной утилиты ps или с помощью утилиты из JDK jps.
dev@inst:/home/dev# jps
14012 ProcessingNetworkData
1236 Kafka
5268 Jps
3926 ProdServerStart
1177 QuorumPeerMain
Определяем объекты доминирующие в памяти.
Для java 8 воспользуемся утилитой jmap из JDK. jmap выведет дамп памяти вашей Java-программы. Увы, работает только с Java 8, если утилита jmap из Java 8 JDK. Утилита считается экспериментальной и в новых JDK не поддерживается. Смотрим статистику по количеству и занимаемой памяти классов
dev@dev:/opt/dev$ sudo jmap -histo 14012
num #instances #bytes class name
----------------------------------------------
1: 413819 50815128 [B
2: 144654 8100624 org.apache.kafka.streams.state.internals.RocksDBRangeIterator
3: 211985 6783520 java.util.HashMap$Node
4: 144940 5797600 java.lang.ref.Finalizer
5: 68322 5353096 [C
6: 144693 4630176 org.rocksdb.RocksIterator
7: 93017 4464816 java.util.HashMap
8: 56104 3141824 java.util.stream.ReferencePipeline$Head
9: 129373 3104952 org.apache.kafka.common.utils.Bytes
10: 129373 3104952 org.apache.kafka.streams.KeyValue
11: 39943 2772248 [Ljava.lang.Object;
12: 42243 2703552 java.util.stream.ReferencePipeline$2
13: 144814 2317024 java.util.concurrent.atomic.AtomicBoolean
14: 37936 2124416 java.util.concurrent.ConcurrentHashMap$KeyIterator
...
Что удерживает ссылки на объекты
Для этого необходимо собрать heapdump. Воспользуемся утилитой jcmd из JDK, формат:
jcmd <pid> GC.heap_dump <file-path>
Как пример наше приложение:
dev@inst:/opt/dev$ jcmd 14012 GC.heap_dump /opt/aqosta/logs/dump.hprof
14012:
Heap dump file created
Для анализа дампа памяти вопользуемся Eclipse Memory Analyzer (MAT)

Смотрим отчет "with outgoing references"

