Примечание: Данная статья является оригинальной, при пересылке необходимо указывать автора и ссылку на оригинал. Добро пожаловать, чтобы следовать[0 Реклама Официальный аккаунт WeChat: Блог Q].
В этой серии три статьи.Эта статья является второй в серии Advanced, в которой подробно объясняются различные инструменты MAT.Основные функции, использование, применимые сценарии, и объяснить в конкретных реальных боевых сценариях, чтобы помочь вам научиться справляться с различными проблемами памяти.
- "Подробное объяснение и практика использования инструмента MAT для анализа памяти JVM — вводная статья》 Знакомство с функциями продукта MAT, основными понятиями, сравнением с другими инструментами, кратким руководством.
- «Углубленное объяснение и практика использования инструмента анализа памяти JVM MAT — расширенная глава»Расширьте и подробно объясните основные функции, использование и сценарии различных инструментов MAT, а также объясните в конкретных реальных боевых сценариях, чтобы помочь вам углубить свой опыт.
- «Углубленное объяснение и практическое применение инструмента MAT для анализа памяти JVM — глава высокого уровня» обобщает метод систематического анализа сложных проблем с памятью и улучшает практические способности каждого с помощью подробного примера.
1. Введение
Владение MAT является важным умением для мастеров Java, но на практике всем часто приходится сталкиваться со многими функциями, и они запутались и не знают, с чего начать Редактор не нашел идеального учебного материала, поэтому я организую эту статью чтобы помочь вам систематически осваивать инструменты анализа MAT.
В этой статье подробно объясняются функции многих инструментов анализа памяти MAT. Комбинация этих функций чрезвычайно мощна, и умелое использование может решить практически все проблемы автономного анализа кучи памяти. Мы делим функции на 4 категории: детали распределения памяти, межобъектные зависимости, детали состояния объекта и выборка по условию. Каждая категория имеет несколько функциональных точек. В этой статье будут описаны сценарии и использование каждой функции по отдельности. Кроме того, для улучшения понимания и мастерства добавляются оригинальные или процитированные случаи.
Примечание: во вступительной статье цикла "Подробное объяснение и практика использования инструмента MAT для анализа памяти JVM — вводная статья» знакомит со сценариями использования и методами установки MAT. Читателям, не знакомым с MAT, рекомендуется сначала прочитать вышеизложенное и установить его. Случай, описанный в этой статье, легко попрактиковать локально. Кроме того, порядок описанной выше части введения продукта также соответствует организации объяснения функций в этой статье, как показано на следующем рисунке:Чтобы уменьшить путаницу по поводу ослепительного меню, вы можете использовать следующий рисунок, чтобы ознакомиться с вводом каждой функции в целом, что будет обсуждаться позже.
2. Подробное объяснение и реальная борьба с распределением памяти
2.1 Обзор глобальной информации
Функции: отображает глобальную статистику, такую как размер динамической памяти, количество объектов, количество классов, количество загрузчиков классов, количество корней GC, переменные среды и обзор потоков.
использовать портал: основной интерфейс MAT → Обзор дампа кучи.
Пример: Ниже приведены количество объектов, количество загрузчиков классов и количество корней GC.Видно, что загрузчик классов ненормальный.
Пример: На следующем рисунке представлен обзор потоков, вы можете просмотреть имя каждого потока, сохраненную кучу потока, атрибуты демона и т. д.
сцены, которые будут использоватьсяОбщий обзор представляет глобальную статистическую информацию, фокусируясь на проверке наличия аномальных данных в целом, поэтому достоверная информация ограничена.Следующие сценарии полезны:
- Когда область метода переполняется (область метода не используется после Java 8, что соответствует переполнению кучи), проверьте, не является ли количество классов ненормально большим, и вы можете рассмотреть, не слишком ли загружен динамический прокси-класс или класс неоднократно загружается неоднократно.
- Когда область метода переполняется, убедитесь, что количество загрузчиков классов слишком велико, и подумайте, не перезапускаются ли пользовательские загрузчики классов ненормально.
- Слишком много GC Roots.Вы можете проверить дистрибутив GC Root.Теоретически такая ситуация встречается редко.Автор столкнулся с ней только когда JNI использовала библиотеку с ошибками.
- Слишком много потоков. Как правило, потоки часто создаются, но не могут быть выполнены. Вы можете понять ненормальный внешний вид из обзора. Конкретные причины см. в части анализа потоков этой статьи, которая не будет здесь расширяться.
2.2 Dominator tree
Примечание. Автор использует Top1 по частоте, что является обязательной функцией для эффективного анализа дампа.
Функции
- Отобразите диаграмму отношения доминирования объекта и укажите размер доминирующей памяти объекта (доминирующая память эквивалентна Retained Heap, то есть размер памяти, который может быть освобожден сборщиком мусора)
- Поддержка сортировки, поддержка кластеризации статистики по пакетам, загрузчику классов, суперклассу, классу
использовать портал: Глобальное дерево доминаторов: основной интерфейс MAT → Дерево доминаторов.
Пример:Глядя на дерево Dominator на рисунке ниже, мы знаем, что в памяти в основном доминируют два потока, ThreadAndListHolder-thread и main (общий случай будет описан в Разделе 2.6 позже).
сцены, которые будут использоваться
-
При запуске анализа дампа вы должны сначала использовать дерево Dominator, чтобы понять размер памяти, в которой преобладают объекты начальной точки каждого доминирующего дерева, а затем понять, какие объекты начальной точки являются причинами, по которым сборщик мусора не может освободить большую память.
-
Когда сохраненная куча дерева доминирования отдельных объектов очень велика и явно перекошена, вы можете сосредоточиться на анализе отношения доминирования объектов с высокой долей и расширить поддерево, чтобы дополнительно найти основную причину проблемы. на рисунке ниже это ArrayList, содержащийся в объекте SameContentWrapperContainer.
-
Развернув древовидную диаграмму в дереве Доминатора, вы можете просмотреть путь отношения доминирования (отличие от исходящей ссылки в том, что если X доминирует над Y, Y должен быть освобожден после освобождения X; если только X ссылается на Y, все еще может быть другие объекты, ссылающиеся на Y , Y по-прежнему не могут быть освобождены после освобождения X, поэтому дерево Dominator удаляет много избыточной информации во входящей ссылке).
-
В некоторых случаях Retained Heap, который не доминирует над начальным объектом, может занимать много памяти (например, класс X имеет 100 объектов, а Retained Heap каждого объекта равен 10M, тогда в памяти фактически доминируют все объекты класса X — 1G, но может случиться так, что дерево Dominator Первые 20 — это объекты других классов), тогда вы можете агрегировать по классу, пакету и загрузчику классов, чтобы найти цель.
-
На рисунке ниже объем памяти, занимаемый каждым GC Roots, невелик, в это время необходимо агрегировать и локализовать точку вспышки.
-
После отображения дерева Dominator оно агрегируется по классам, как показано ниже:
-
Можно обнаружить, что объект SomeEntry занимает больше памяти, а затем дополнительно анализирует конкретную причину в сочетании с кодом.
-
-
После обнаружения исключения, удерживающего объект Retained Heap после некоторых операций (например, объект должен быть переработан из кода), можно получить прямой доминатор объекта.Метод операции следующий.
2.3 Гистограмма Гистограмма
Примечание: автор использует частоту Top2
Функции
- Укажите номер каждого экземпляра класса и совокупную долю памяти экземпляра класса, включая его собственное использование памяти (неглубокая куча) и использование памяти доминирующим объектом (сохранение кучи).
- Поддержка сортировки по количеству объектов, Retained Heap, Shallow Heap (сортировка по умолчанию) и другим показателям; поддержка фильтрации по регулярности; поддержка кластеризации статистики по пакету, загрузчику классов, суперклассу, классу,
использовать портал: основной интерфейс MAT → Гистограмма; обратите внимание, что гистограмма не отображает Retained Heap по умолчанию, вы можете использовать значок калькулятора для расчета, как показано на следующем рисунке.
сцены, которые будут использоваться
- В некоторых случаях дерево Dominator не может отображать активные объекты (как упоминалось выше, доля топ-20 доминирующей памяти дерева Dominator невелика, или отсутствуют очевидные активные объекты, агрегированные по классам. В настоящее время это сложно для дерева Dominator сделать анализ ассоциации, чтобы определить, какой тип.Доля объектов высока), то вы можете использовать гистограмму, чтобы просмотреть распределение классов, к которым принадлежат все объекты, и быстро найти классы, которые занимают большую часть сохраненной кучи.
- навыки и умения
- Integer, String и Object[] обычно напрямую не вызывают проблем с памятью. Чтобы лучше организовать представление, дальнейший фокус можно сгруппировать по загрузчику классов или пакету, как показано ниже.
- Гистограмма поддерживает фильтрацию с использованием регулярных выражений. Например, мы могли бы показать только те классы, которые соответствуют com.q.*.
- Вы можете продолжать использовать исходящую ссылку в классе гистограммы для просмотра распределения объектов, а затем определить, какие объекты являются большими.
2.4 Leak Suspects
Функции: Он имеет функцию автоматического обнаружения утечек памяти и перечисляет проблемные точки, в которых могут быть утечки памяти.
использовать портал: Как правило, при очевидной утечке памяти она будет отображаться после анализа файла дампа, или вы можете перейти на домашнюю страницу MAT → Подозрения на утечку, как показано ниже.
сцены, которые будут использоваться: Нужно посмотреть подозрительные объекты, занимающие много памяти в цепочке ссылок. Эта функция может решить некоторые основные проблемы, но сложные проблемы часто не помогают.
Пример
-
Представление Leak Suspects на рисунке ниже показывает, что большую часть памяти занимают два потока.
-
На следующем рисунке показаны сведения о кратчайшем пути и пути доминатора от экземпляра к корневому каталогу сборщика мусора.
2.5 Top Consumers
Функции: отчет о самых больших объектах, который может показать, какие классы, какие загрузчики классов и какие пакеты занимают наибольшую долю памяти, а также поддерживаются его функции Histogram и Dominator tree.
сцены, которые будут использоваться: когда в приложении происходит утечка памяти, проверка того, какие объекты утечки обычно составляют большую часть моментального снимка дампа. Поэтому он имеет большое значение для простых задач.
2.6 Комплексный случай 1
Используйте элементы инструментов: обзор дампа кучи, дерево Dominator, гистограмма, обозреватель загрузчика классов (см. раздел 3.4), входящие ссылки (см. раздел 3.1)
код
package com.q.mat;
import java.util.*;
import org.objectweb.asm.*;
public class ClassLoaderOOMOps extends ClassLoader implements Opcodes {
public static void main(final String args[]) throws Exception {
new ThreadAndListHolder(); // ThreadAndListHolder 类中会加载大对象
List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
final String className = "ClassLoaderOOMExample";
final byte[] code = geneDynamicClassBytes(className);
// 循环创建自定义 class loader,并加载 ClassLoaderOOMExample
while (true) {
ClassLoaderOOMOps loader = new ClassLoaderOOMOps();
Class<?> exampleClass = loader.defineClass(className, code, 0, code.length); //将二进制流加载到内存中
classLoaders.add(loader);
// exampleClass.getMethods()[0].invoke(null, new Object[]{null}); // 执行自动加载类的方法,通过反射调用main
}
}
private static byte[] geneDynamicClassBytes(String className) throws Exception {
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_1, ACC_PUBLIC, className, null, "java/lang/Object", null);
//生成默认构造方法
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
//生成构造方法的字节码指令
mw.visitVarInsn(ALOAD, 0);
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mw.visitInsn(RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
//生成main方法
mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
//生成main方法中的字节码指令
mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mw.visitLdcInsn("Hello world!");
mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mw.visitInsn(RETURN);
mw.visitMaxs(2, 2);
mw.visitEnd(); //字节码生成完成
return cw.toByteArray(); // 获取生成的class文件对应的二进制流
}
}
package com.q.mat;
import java.util.*;
import org.objectweb.asm.*;
public class ThreadAndListHolder extends ClassLoader implements Opcodes {
private static Thread innerThread1;
private static Thread innerThread2;
private static final SameContentWrapperContainerProxy sameContentWrapperContainerProxy = new SameContentWrapperContainerProxy();
static {
// 启用两个线程作为 GC Roots
innerThread1 = new Thread(new Runnable() {
public void run() {
SameContentWrapperContainerProxy proxy = sameContentWrapperContainerProxy;
try {
Thread.sleep(60 * 60 * 1000);
} catch (Exception e) {
System.exit(1);
}
}
});
innerThread1.setName("ThreadAndListHolder-thread-1");
innerThread1.start();
innerThread2 = new Thread(new Runnable() {
public void run() {
SameContentWrapperContainerProxy proxy = proxy = sameContentWrapperContainerProxy;
try {
Thread.sleep(60 * 60 * 1000);
} catch (Exception e) {
System.exit(1);
}
}
});
innerThread2.setName("ThreadAndListHolder-thread-2");
innerThread2.start();
}
}
class IntArrayListWrapper {
private ArrayList<Integer> list;
private String name;
public IntArrayListWrapper(ArrayList<Integer> list, String name) {
this.list = list;
this.name = name;
}
}
class SameContentWrapperContainer {
// 2个Wrapper内部指向同一个 ArrayList,方便学习 Dominator tree
IntArrayListWrapper intArrayListWrapper1;
IntArrayListWrapper intArrayListWrapper2;
public void init() {
// 线程直接支配 arrayList,两个 IntArrayListWrapper 均不支配 arrayList,只能线程运行完回收
ArrayList<Integer> arrayList = generateSeqIntList(10 * 1000 * 1000, 0);
intArrayListWrapper1 = new IntArrayListWrapper(arrayList, "IntArrayListWrapper-1");
intArrayListWrapper2 = new IntArrayListWrapper(arrayList, "IntArrayListWrapper-2");
}
private static ArrayList<Integer> generateSeqIntList(int size, int startValue) {
ArrayList<Integer> list = new ArrayList<Integer>(size);
for (int i = startValue; i < startValue + size; i++) {
list.add(i);
}
return list;
}
}
class SameContentWrapperContainerProxy {
SameContentWrapperContainer sameContentWrapperContainer;
public SameContentWrapperContainerProxy() {
SameContentWrapperContainer container = new SameContentWrapperContainer();
container.init();
sameContentWrapperContainer = container;
}
}
启动参数:-Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/gjd/Desktop/dump/heapdump.hprof
-XX:-UseCompressedClassPointers -XX:-UseCompressedOops
Диаграмма цитирования
Процесс анализа
- Сначала войдите в дерево Dominator, можно увидеть, что объект SameContentWrapperContainerProxy и основной поток удерживают 99% памяти и не могут быть освобождены, что приводит к OOM.
- Давайте сначала посмотрим на направление 1. В обзоре дампа кучи вы можете быстро найти количество загрузчиков классов более 500 000. Это в основном ненормальная ситуация, как показано на следующем рисунке.
- Используя инструмент анализа Class Loader Explorer, в это время будут отображаться сведения о загрузке классов, и вы можете увидеть, что существует 524061 загрузчик классов. В нашем случае есть только пользовательские загрузчики классов, такие как ClassLoaderOOMOps, поэтому проблему можно быстро локализовать.
- Если имеется много загрузчиков классов и вы не можете определить, какой из них вызывает проблему, вы можете сгруппировать все объекты загрузчиков классов по классам, как показано на следующем рисунке.
- Гистограмма будет агрегирована в соответствии с классом и покажет неглубокую кучу и сохраненную кучу объекта по порядку величины (если элемент Retained Heap пуст, вы можете щелкнуть значок компьютера на рисунке ниже, чтобы вычислить Retained Heap). ), вы можете видеть, что ClassLoaderOOMOps имеет 524044 объекта, а его Retained Heap занимает более 370M (в приведенном выше коде около 100M).
- Используя входящие ссылки, можно найти место созданного кода.
- Давайте посмотрим на направление 2, также в массиве объектов, занимающем 319 МБ памяти, используя входящие ссылки для просмотра пути ссылки, также легко найти конкретное место кода. А из рисунка ниже мы видим, что отправной точкой дерева Доминаторов не обязательно является корень GC, и начальный путь создания может быть получен не через дерево Доминаторов, но возможны входящие ссылки.
3. Подробное объяснение зависимости между объектами и фактический бой
3.1 References
Примечание: автор использует частоту Top2
Функции: просмотр всех ссылочных связей определенного объекта в графе ссылок на объекты (укажите ссылочную связь объекта с другими объектами или базовыми типами, а также ссылочную связь других внешних объектов). Предоставляет полные сведения о связи зависимостей посредством прямых и косвенных ссылок на любой объект (в основном значения атрибутов и объем памяти).
использовать портал: Щелкните правой кнопкой мыши на целевом домене → Список объектов → с исходящими ссылками/с входящими ссылками.
сцены, которые будут использоваться
- исходящая ссылка: просмотр объектов, на которые ссылается объект, и поддержка связанных сквозных операций. Например, чтобы увидеть, какое содержимое содержит большой объект, когда Retained Heap сложного объекта велик, вы можете увидеть, какое свойство вызвано исходящей ссылкой. На рисунке ниже A доминирует над F, а F занимает большой объем памяти, но объект прямого управления A для F не может быть изменен во время оптимизации. Вы можете увидеть D, B, E и C в цепочке отношений через исходящие ссылки и оптимизировать промежуточные звенья с помощью бизнес-логики, что невозможно сделать, полагаясь на дерево доминаторов.
- входящая ссылка: просмотр объектов, на которые ссылается объект, и поддержка операций последовательного прохода. Например, чтобы проверить, на какие объекты ссылается большой объект, на следующем рисунке K занимает большой объем памяти, поэтому Retained Heap J больше, и цель состоит в том, чтобы удалить ссылку на J из корней GC. , но в дереве Dominator J является корнем дерева, и получить его невозможно. Для пути ссылки можно просмотреть H, X и Y в цепочке отношений через входящую ссылку и удалить J из GC Корневая цепочка в сочетании с бизнес-логикой.
3.2 Thread overview
Функции: отображает подробное состояние стека выполнения потоков и объекты, на которые ссылается стек потоков при создании дампа файла дампа, а также предоставляет связанную информацию о памяти, такую как сохраненная куча каждого потока.
использовать портал: Домашняя страница MAT → Обзор темы
сцены, которые будут использоваться
- Проверьте долю памяти, занимаемую различными потоками, и найдите потоки с высоким потреблением памяти (навыки разработки: не используйте имена потоков по умолчанию Thread или Executor напрямую, чтобы не смешивать их все вместе. Используйте потоки, чтобы назвать их как можно больше для простоты идентификация ThreadAndListHolder-поток на следующем рисунке является автоматическим потоком Определите имя потока, вы можете легко найти конкретный код)
- Просмотрите стек выполнения и переменные потока и объедините бизнес-код, чтобы понять, где поток заблокирован, и память не может быть освобождена, если он не может продолжать работу.Как показано на рисунке ниже, ThreadAndListHolder-поток заблокирован в метод сна.
3.3 Path To GC Roots
Функции: Предоставляет сведения о пути для любого объекта к корневому каталогу GC.
использовать портал: Щелкните правой кнопкой мыши целевой домен → Путь к корням GC
сцены, которые будут использоваться: Иногда вы уверены, что большой набор объектов был обработан, но все еще не может быть восстановлен.Эта функция может быстро найти причину, по которой ненормальный объект не может быть восстановлен сборщиком мусора, и напрямую указать путь от ненормального объекта к сборщику мусора. Корень. Преимущество перед входящей ссылкой заключается в том, что она скрывает множество отношений ссылок, не требующих внимания, а преимущество перед деревом Доминатора заключается в том, что можно получить более полную информацию.
Совет. При устранении утечек памяти рекомендуется исключить все фантомные/слабые/мягкие и т. д. ссылки, чтобы исключить цепочки ссылок, такие как виртуальные ссылки/слабые ссылки/мягкие ссылки, поскольку объекты, на которые ссылаются виртуальные/слабые/мягкие ссылки, могут быть напрямую к которым обращается сборщик мусора, обращая внимание на то, есть ли у объекта цепочка ссылок Strong.
3.4 анализ загрузчика классов
Функции
- Просмотрите использование всех загрузчиков классов в куче (вход: значок синего ведра в главном меню MAT → Основы Java → Обозреватель загрузчика классов).
- Просмотрите классы в куче, которые неоднократно загружаются разными загрузчиками классов (вход: синий значок ведра в главном меню MAT → Основы Java → Дублированные классы).
сцены, которые будут использоваться
- Он используется, когда из обзора дампа кучи известно, что в системе слишком много загрузчиков классов, что приводит к ненормальному использованию памяти и требует более подробного анализа для определения основной причины.
- Решите проблему NoClassDefFoundError или определите, загружается ли пакет jar повторно.
Конкретный метод использования описан в случаях в разделах 2.6 и 3.5.
3.5 Комплексный случай 2
Используйте элементы инструментов: загрузчик классов (обнаружение повторяющихся классов), инспектор, регулярный поиск.
необычное явление: NoClassDefFoundError во время выполнения существуют две разные версии класса с одинаковым именем в пути к классам.
Процесс анализа
-
Введите загруженную функцию обнаружения повторяющихся классов MAT, как показано на рисунке ниже.
-
Вы можете увидеть все дублированные классы, а также связанные с ними загрузчики классов, как показано ниже.
-
Судя по названию класса, в
<Regex>
Введите имя класса в поле, чтобы отфильтровать недопустимую информацию. -
Выберите целевой класс, и в представлении Inspector вы сможете увидеть, в каком пакете jar находится загруженный класс. (Дублированный класс в этом примере загружается с помощью URLClassloader, щелкните правой кнопкой мыши свойство «_context» и, наконец, нажмите «Перейти к», значение свойства «_war» во всплывающем окне — это конкретное расположение загруженного пакета. класс)
4. Подробное объяснение и фактический бой статуса объекта
4.1 inspector
Функции: MAT отображает подробную информацию об объекте через панель инспектора, такую как значение статического атрибута и значение атрибута экземпляра, адрес памяти, отношения наследования классов, пакет, загрузчик классов, корни GC и другие подробные данные.
сцены, которые будут использоваться
- Когда использование памяти сильно связано с бизнес-логикой, инспектор можно использовать для просмотра конкретных значений свойств объекта. Например, список друзей пользовательского объекта в социальной сцене является ненормальным, а длина списка достигает сотен миллионов.Ненормальный идентификатор пользователя получается через панель инспектора, а затем продолжается проверка того, какой пользователь принадлежит из с точки зрения бизнеса.В этом примере может быть системная учетная запись, которая связана со всеми пользователями, являющимися друзьями.
- Коллекции и другие типы используются больше, например, просмотрев свойство размера ArrayList, можно понять его размер.
Пример: Окно Inspector слева на рисунке ниже показывает детали экземпляра ArrayList по адресу 0x125754cf8, включая основные свойства, такие как modCount, которые не отображаются в исходящих ссылках.
4.2 Состояние коллекции
Функции: помогает понять использование памяти системой более интуитивно и найти неиспользуемое пространство памяти.
использовать портал: Главная страница MAT → Коллекции Java → Конфликт скорости заполнения/хэширования и другие функции.
сцены, которые будут использоваться
- Найдите потери памяти, вызванные разреженными или пустыми объектами коллекции, путем кластеризации объектов коллекции, таких как ArrayList или массивы, по скорости заполнения.
- Определите, является ли стратегия хеширования разумной, с помощью коэффициента конфликта HashMap.
Конкретный метод использования подробно описан в Разделе 4.3.
4.3 Комплексный случай 3
Используйте элементы инструментов: Дерево доминаторов, гистограмма, заданное соотношение.
необычное явление: Программа OOM, а дерево Dominator не имеет больших объектов.Из гистограммы известно, что несколько ArrayList занимают большой объем памяти, и ожидается оптимизация программы за счет уменьшения ArrayList.
код
package com.q.mat;
import java.util.ArrayList;
import java.util.List;
public class ListRatioDemo {
public static void main(String[] args) {
for(int i=0;i<10000;i++){
Thread thread = new Thread(new Runnable() {
public void run() {
HolderContainer holderContainer1 = new HolderContainer();
try {
Thread.sleep(1000 * 1000 * 60);
} catch (Exception e) {
System.exit(1);
}
}
});
thread.setName("inner-thread-" + i);
thread.start();
}
}
}
class HolderContainer {
ListHolder listHolder1 = new ListHolder().init();
ListHolder listHolder2 = new ListHolder().init();
}
class ListHolder {
static final int LIST_SIZE = 100 * 1000;
List<String> list1 = new ArrayList(LIST_SIZE); // 5%填充
List<String> list2 = new ArrayList(LIST_SIZE); // 5%填充
List<String> list3 = new ArrayList(LIST_SIZE); // 15%填充
List<String> list4 = new ArrayList(LIST_SIZE); // 30%填充
public ListHolder init() {
for (int i = 0; i < LIST_SIZE; i++) {
if (i < 0.05 * LIST_SIZE) {
list1.add("" + i);
list2.add("" + i);
}
if (i < 0.15 * LIST_SIZE) {
list3.add("" + i);
}
if (i < 0.3 * LIST_SIZE) {
list4.add("" + i);
}
}
return this;
}
}
Процесс анализа
- Используйте дерево Dominator, чтобы увидеть, что нет начальной точки с высоким процентом.
- Использование гистограммы для определения местоположения ListHolder и ArrayList составляет большую долю.После бизнес-анализа многие списки имеют низкую скорость заполнения и трату памяти.
- Проверьте скорость заполнения ArrayList, MAT Home → Коллекции Java → Коэффициент заполнения коллекции.
- Проверьте тип для заполнения java.util.ArrayList.
- Из результатов видно, что начальная длина приложения большинства ArrayList слишком велика.
5. Поиск по условию для подробного объяснения и фактического боя
5.1 OQL
Функции: Предоставляет унифицированный структурированный язык запросов на уровне объекта (класса), аналогичный SQL, для фильтрации объектов в куче в соответствии с условиями.
грамматика
SELECT * FROM [ INSTANCEOF ] <class_name> [ WHERE <filter-expression> ]
- Предложение Select может использовать «*» для просмотра экземпляра ссылки объекта результата (эквивалентно исходящим ссылкам); вы можете указать конкретное содержимое, например Select OBJECTS v.elementData from xx, возвращаемый результат является полным объектом, а не простое описание объекта); вы можете использовать ключевое слово Distinct для дедупликации.
- From указывает диапазон запроса, обычно указывая имя класса, регулярное выражение и адрес объекта.
- Где используется для указания условий фильтра.
- Полную грамматику см.OQL-синтаксис
- Неподдерживаемая основная функция: группировка по значению, при необходимости можно сначала экспортировать результаты в csv, а затем использовать инструменты-скрипты, такие как awk, для анализа.
- пример: Найдите неиспользуемый список ArrayList с размером = 0: выберите * из java.util.ArrayList, где размер = 0 и modCount = 0.
сцены, которые будут использоваться
- Как правило, OQL используется для более сложных задач, и такие проблемы часто связаны с бизнес-логикой. Например, большое количество мелких объектов занимает большую общую память, но предполагается, что мелких объектов не должно быть слишком много (например, достигающих миллиона), и просмотреть их по одному нереально. можно использовать запрос OQL для экспорта данных для исследования.
- пример:Распределенная система отслеживания ссылок микросервисов собирает все имена интерфейсов каждого сервиса.Всего 200 сервисов собрали 2 млн имён интерфейсов (у сервиса не будет 10000 интерфейсов).В это время один за другим непосредственно в Просмотре списка находится трудно найти, вы можете напрямую использовать OQL для экспорта, определить, какое имя интерфейса службы собирает исключения (например, идентификатор в URL-адресе также учитывается в интерфейсе)
5.2 Поиск и проверка
Функции: Распределение памяти в главе 2 этой статьи и многие функции, зависящие от объектов в главе 3, поддерживают такие операции, как извлечение строк и обычное извлечение.
сцены, которые будут использоваться: при использовании гистограммы, обзора потоков и других функций можно дополнительно добавить сопоставление строк и обычный фильтр сопоставления, чтобы сузить область исследования.
5.3 Адресация по адресу
Функции: поиск объекта по его шестнадцатеричному адресу виртуальной памяти.
сцены, которые будут использоваться: Используется только тогда, когда вы знаете адрес и хотите быстро просмотреть объект для последующего анализа.В остальном вы можете напрямую использовать исходящий справочник для понимания информации об объекте.
5.4 Комплексный случай 4
Используйте элементы инструментов: OQL, гистограмма, входящие ссылки
Аномалии и цель: Программа занимает много памяти, и есть ArrayList с долгой инициализацией по умолчанию.Необходимо проанализировать долю используемого ArrayList, использовать ли режим ленивой загрузки через поддержку данных, и проанализировать, какой код создает пустой список массивов.
код
public class EmptyListDemo {
public static void main(String[] args) {
EmptyValueContainerList emptyValueContainerList = new EmptyValueContainerList();
FilledValueContainerList filledValueContainerList = new FilledValueContainerList();
System.out.println("start sleep...");
try {
Thread.sleep(50 * 1000 * 1000);
} catch (Exception e) {
System.exit(1);
}
}
}
class EmptyValueContainer {
List<Integer> value1 = new ArrayList(10);
List<Integer> value2 = new ArrayList(10);
List<Integer> value3 = new ArrayList(10);
}
class EmptyValueContainerList {
List<EmptyValueContainer> list = new ArrayList(500 * 1000);
public EmptyValueContainerList() {
for (int i = 0; i < 500 * 1000; i++) {
list.add(new EmptyValueContainer());
}
}
}
class FilledValueContainer {
List<Integer> value1 = new ArrayList(10);
List<Integer> value2 = new ArrayList(10);
List<Integer> value3 = new ArrayList(10);
public FilledValueContainer init() {
value1.addAll(Arrays.asList(1, 3, 5, 7, 9));
value2.addAll(Arrays.asList(2, 4, 6, 8, 10));
value1.addAll(Arrays.asList(1, 1, 1, 1, 1, 1, 1, 1, 1, 1));
return this;
}
}
class FilledValueContainerList {
List<FilledValueContainer> list = new ArrayList(500);
public FilledValueContainerList() {
for (int i = 0; i < 500; i++) {
list.add(new FilledValueContainer().init());
}
}
}
Процесс анализа
-
В памяти имеется 500 000 пустых экземпляров ArrayList с емкостью, равной 10. Мы анализируем общий размер памяти этих объектов и место создания объекта, чтобы проанализировать, нужна ли ленивая инициализация (то есть не создание экземпляров этих объектов до тех пор, пока они не будут использованы, иначе он всегда будет нулевым).
-
Используйте OQL для запроса неиспользуемого ArrayList после инициализации (size=0 и modCount=0) Оператор показан ниже. Видно, что в открытом доступе 1,5 миллиона пустых ArrayList, и эти объекты — пустая трата памяти. Затем мы вычисляем, сколько памяти в целом занято, и смотрим, нужна ли оптимизация на основе результатов.
-
Чтобы вычислить общий объем памяти, занимаемый 1,5 миллионами списков массивов, щелкните значок гистограммы с желтой стрелкой в правом верхнем углу. на результат здесь, исключая объекты modCount или ArrayList, размер которых больше 0).Этот тип непрерывного анализа выбранных результатов поддерживает множество функций, таких как обычный поиск, гистограмма, дерево доминаторов и т. д.
-
Проверьте конкретный источник пустого ArrayList ниже, доступные входящие ссылки, на следующем рисунке показан четкий путь создания объекта.
Сводная перспектива
До сих пор в этой статье объяснялись функции, методы использования и применимые сценарии различных инструментов MAT, а также чередовались с реальными боевыми случаями 4. Умение анализировать проблемы с памятью JVM очень полезно, особенно сочетание различных функций. В следующей статье «Углубленное объяснение и практика использования инструмента анализа памяти JVM MAT — расширенная глава» будет обобщен систематический метод анализа памяти кучи JVM и практика в более сложных случаях.
Справочное содержание
- официальный сайт МАТ
- 10 Tips for using the Eclipse Memory Analyzer
- Finding Memory Leaks with SAP Memory Analyzer
- An effective way to fight duplicated libs and version conflicting classes using Memory Analyzer Tool
- Память даром (проблема с пустой коллекцией)
Добро пожаловать в пересылку, обратите внимание, общедоступный номер автора WeChat:Блог Q.Время от времени отправляйте галантерею, практический опыт, сводку системы, интерпретацию исходного кода, технические принципы.