План этой статьи
- Первый этап:Введение в основные инструменты JVMподробное введение
- Этап 2: JVMИдеи оптимизации
- Этап 3: JVMРеальный тюнинг кейс
Фаза 1: Знакомство с основными инструментами JVM
jmap ---> инструмент сопоставления памяти Java
jmapMemory Map for JavaИспользуется для создания моментальных снимков дампа кучиОбычно известен как heapdump или файл дампа.. В то же время он также может запрашивать сведения об очереди выполнения финализации, куче Java и области методов, такие как использование пространства, какой сборщик используется в данный момент и т. д.
Проще говоря, его можно использовать дляПросмотр информации о динамической памяти
формат команды jmap
jmap option vmid
вариант вариант
jmap использует демонстрационный пример
Пример: Все случайныСоздайте проект spring-boot и добавьте веб-модуль для запускаВот и все, я не буду здесь подробно описывать шаги.
Шаг 1: Запустите созданный проект spring-boot
Шаг 2: Терминал ввода команды jps
jpsJava Virtual Machine Process Status ToolВся Java — это текущий процесс, отображаемый командой pid, предоставляемой Java.
Найдите соответствующий номер процесса Java:49150
Шаг 3: Введите команду в терминале в соответствии с форматом команды jmap для просмотра
Первая команда: jmap -histo номер процесса
входитьjmap -histo 49150Взгляните на результаты:
Обнаружено, что его нельзя отобразить в терминале, а печатать слишком много, поэтому выводим распечатанный контент в файл
входитьjmap -histo 49150 > ./jmapLog.txtВзгляните на результаты:
Мы обнаружили, что такой файл был сгенерирован в соответствующем пути к файлу.jmapLog.txtфайл, открываем его и видим:
можно увидеть три штукиКрайний левый номер можно игнорировать, справа налево идут
- имя класса: имя класса
- байты: количество байтов
- instances: количество экземпляров
Другими словами, распечататьсколько экземпляров каждого классаОбщее количество занятых байтовили объем памяти
Вторая команда: jmap -номер процесса кучи
Согласно таблице выше: Эта команда используется дляОтображение сведений о куче Java, например, какой сборщик мусора используется, конфигурация параметров, статус генерации и т. д., давайте распечатаем
входитьjmap -heap 49150Взгляните на результаты:
Если у вас такая же проблема, как у меня, вы можете прочитать эту статью, она должна решить вашу проблему, нет, конечно, лучше всего:
После корректировки в приведенной выше статье окончательный вводjhsdb jmap --heap --pid 49150печатать после команды:
$ jhsdb jmap --heap --pid 49150
Attaching to process ID 49150, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.11+9-LTS-194
using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 1287651328 (1228.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 2048
capacity = 2147483648 (2048.0MB)
used = 14274048 (13.61279296875MB)
free = 2133209600 (2034.38720703125MB)
0.6646871566772461% used
G1 Young Generation:
Eden Space:
regions = 2
capacity = 69206016 (66.0MB)
used = 2097152 (2.0MB)
free = 67108864 (64.0MB)
3.0303030303030303% used
Survivor Space:
regions = 7
capacity = 7340032 (7.0MB)
used = 7340032 (7.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 5
capacity = 57671680 (55.0MB)
used = 4836864 (4.61279296875MB)
free = 52834816 (50.38720703125MB)
8.386896306818182% used
Первая часть содержимого: информация о конфигурации кучи
-
MinHeapFreeRatio: Минимальный процент свободного места в кучеФормула расчета: Heapfreeratio = (cealfreeheapsize / centrettalheapsize) * 100 диапазон значения составляет от 0 до 100,По умолчанию 40. Если HeapFreeRatio
-
MaxHeapFreeRatio: Максимальный процент свободного места в куче, формула расчета: HeapFreeRatio = (CurrentFreeHeapSize/CurrentTotalHeapSize) * 100, диапазон значений от 0 до 100,По умолчанию 70. Если HeapFreeRatio > MaxHeapFreeRatio, вам нужно уменьшить кучу, и время сжатия должно быть после каждой сборки мусора.
-
MaxHeapSize: максимальная куча памяти
-
NewSize: Новое поколение оккупированных пространств
-
MaxNewSize: Максимум свободного места для молодого поколения
-
OldSize: Старость занимает место
-
NewRatio: Доля нового поколения, 2 означает 1:2, что составляет одну треть
-
SurvivorRatio: Соотношение двух областей Выживших и области Эдема, 8 означает 2 Выживших:Эдем = 2:8, что означает, что на одну область Выживших приходится 1/10 молодого поколения.
-
Размер метапространства:Размер метапространства
-
Сжатый класспространственный размер:Размер области сжатия указателя
-
MaxMetaspaceSize: Максимальный размер метапространства
-
G1HeapRegionSize: Размер одного региона в сборщике мусора G1
Вторая часть контента: используемая информация о куче
Поскольку я использую jdk 11, по умолчанию используется сборщик мусора G1, куча памяти и структура печати jdk 8 разные, мы идем в соответствии с их анализом реальной сцены, и мне не нужно было быть последовательным
-
G1 Куча: общее пространство кучи,регионы - количество регионов, по умолчанию 2048,емкость - это общий размер кучи,used - это пространство кучи, которое было использовано,Свободное место в куче не используетсяи, наконец, используйте масштаб
-
G1 Молодое поколение: Молодое космическое подразделение / размер молодого поколения, т.е.Разделен на зону Эдема и зону выживших., размер каждой области здесь подробно описываться не будет, просто посмотрите на картинку прямо
-
G1 Old Generation: старая космическая часть / размер старого поколения
Третья команда: jmap -dump номер процесса
Например: реализация некоторыхjmap -dump:format=b,file=jmapDump 49150
Если он найден, в соответствующем каталоге будет создан информационный файл моментального снимка кучи с именем jmapDump.
Настройте автоматические загрузки
Так же можем настроить автоматическую загрузку, автоматически сбрасывать файлы при переполнении памяти
Советы:Если памяти очень много, ее нельзя экспортировать.
- -XX:+HeapDumpOnOutOfMemoryErrorНастроить автоматический экспорт
- -XX:HeapDumpPath=./xxx/xxx/xxxдорожка
Посмотреть примеры jmapDump
Создайте новый тестовый класс в тестовом каталогеЭто создание объекта в бесконечном цикле, и пусть этот объект всегда будет ссылаться на GC Roots и не будет переработан.
плюс-XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPathЗапускать после этих двух команд, автоматически сбрасывать при переполнении памяти
// 测试代码
public class DumpTest {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
int i = 0;
int j = 0;
while (true) {
list.add(new Teacher("teacher" + i++, i++));
new Teacher("teacher" + j--, j--);
}
}
}
class Teacher {
private String name;
private Integer age;
public Teacher(String name, Integer age) {
this.name = name;
this.age = age;
}
}
Добавляем параметры JVM в конфигурацию идеи, для тех, у кого нет, выбираем столбец параметров JVM по красному окошку ниже.
-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumpTest.log
-Xms10m -xmx10m - генерировать OOM как можно скорее. После настройки параметров выполните его и обнаруживаем, что OOM создается.
Затем я поискал файл dumpTest.log и обнаружил, что он был сгенерирован автоматически, так что проблема приближается, мыКак анализировать этот документ?
jvisualvm
Инструмент, поставляемый с jdk, может просматривать этот файл, импортируя его.
Советы:Личный тест, jdk11 не поддерживает этот инструмент, потому что jdk на моем компьютере является двойной версией, поэтому, когда я демонстрирую здесь, я переключаюсь на jdk8, а затем открываю jvisualvm
Мы введем в терминалjvisualvmПосле открытия инструмента загрузите файл по файлу в открытый dumptest.log
вы можете видеть, что естьОсновная информация, среда, системные свойства и информация о потоках в моментальных снимках кучи
Переключаемся в окно класса
ЗнатьКакие классы имеют наибольшее количество экземпляров, и каков размер и пропорция занимаемого пространства?Таким образом, мы можем четко знать распределение экземпляров классов в куче. Если мы обнаружим, что экземпляров определенного класса слишком много, мы можем найти место, где создается экземпляр этого класса, и проверить. используйте этот метод, чтобы узнатьПричина JVM памяти парят
Остальные команды jmap и оставшиеся три команды jmap здесь не будут продемонстрированы, поскольку они не используются повсеместно.
jstack ---> Инструмент трассировки стека Java
jstackStack Trace for JavaКоманда используется для генерации текущего момента JVMснимок потока
снимок потока
Моментальный снимок потокаКоллекция стеков методов, выполняемых каждым потоком в текущей JVM.Для генерации снимка цели часто нитьНайдите причины длительных пауз в потоках, такие как взаимоблокировки между потоками, бесконечные циклы, длительная приостановка, вызванная запросами внешних ресурсов и т. д., являются распространенными причинами длительных пауз в потоках..
Когда нить застопориласьПросмотр стека вызовов каждого потока через jstack, вы можете узнать, что неотвечающий поток делает в фоновом режиме или какие ресурсы ждут.
формат команды jstack
jstack option vmid
jstack пример
Поскольку он используется для определения причины длительной паузы потоков, такой как взаимоблокировка между потоками, мы моделируем взаимоблокировку потока.Ниже приведен простой класс взаимоблокировки:
/**
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
*/
public class DeadLockTest implements Runnable{
public int flag = 1;
//静态对象是类的所有对象共享的
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
System.out.println("flag=" + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
DeadLockTest td1 = new DeadLockTest();
DeadLockTest td2 = new DeadLockTest();
td1.flag = 1;
td2.flag = 0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}
Давайте запустим его и обнаружим, что программа застряла здесь и не может продолжать работу.
Наша программа не завершается, используйте команду jstack, чтобы узнать, как устранить неполадки, первый проходКоманда jps находит процесс java
затем используйтеномер процесса jstack, чтобы увидеть
2021-04-27 23:42:14
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):
"Attach Listener" #15 daemon prio=9 os_prio=31 tid=0x00007fc476153800 nid=0x320b waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #14 prio=5 os_prio=31 tid=0x00007fc4761b1800 nid=0x1003 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=31 tid=0x00007fc475108800 nid=0x3e03 waiting for monitor entry [0x0000700002560000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:39)
- waiting to lock <0x0000000795946928> (a java.lang.Object)
- locked <0x0000000795946938> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #12 prio=5 os_prio=31 tid=0x00007fc4768b8800 nid=0x3c03 waiting for monitor entry [0x000070000245d000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:27)
- waiting to lock <0x0000000795946938> (a java.lang.Object)
- locked <0x0000000795946928> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=31 tid=0x00007fc476152800 nid=0x4203 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007fc476152000 nid=0x4403 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007fc476151000 nid=0x3903 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007fc47687c800 nid=0x4503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007fc475032000 nid=0x4603 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007fc475031000 nid=0x4703 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007fc47582e800 nid=0x4807 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc47582a000 nid=0x4903 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc475022000 nid=0x5103 in Object.wait() [0x0000700001939000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:212)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fc476033800 nid=0x5203 in Object.wait() [0x0000700001836000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=31 tid=0x00007fc476014800 nid=0x2b03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fc47501b800 nid=0x1f07 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fc47501c000 nid=0x2103 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fc47501c800 nid=0x2303 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fc47501d800 nid=0x2a03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fc475962000 nid=0x3b03 waiting on condition
JNI global references: 1579
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007fc4758254a8 (object 0x0000000795946928, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007fc475821408 (object 0x0000000795946938, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:39)
- waiting to lock <0x0000000795946928> (a java.lang.Object)
- locked <0x0000000795946938> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:27)
- waiting to lock <0x0000000795946938> (a java.lang.Object)
- locked <0x0000000795946928> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
Вся информация о потоке в процессе печатается, например, следующие два потока:
- Тема-1: имя темы
- приоритет: приоритет
- os_prio: приоритет потока на уровне операционной системы
- tid: идентификатор потока
- nid: поток соответствует идентификатору локального потока.
- java.lang.Thread.State: состояние потока
Прочитайте до конца информацию о печати, мы нашли помощь jstack, мы нашли тупик, сказали, что тупик нашел уровень Java
Мало того, jstack также помогает нам вывести причины взаимоблокировки, такие как следующие, из-заПоток-1 ожидает получения блокировки 0x00007fc4758254a8, но блокировка теперь удерживается потоком-0, а поток-0 ожидает получения блокировки 0x00007fc475821408, но блокировка теперь удерживается потоком-1.
Помимо вывода причины взаимоблокировки, это также помогло нам определить количество строк кода в java:
jvisualvm обнаруживает взаимоблокировку
Мы также можем быстро обнаруживать взаимоблокировки с помощью jvisualvm, как показано на следующем рисунке.
рядом, поблизостиДамп ниткиЭто для выполнения идентификатора процесса jstack, о котором мы упоминали выше.
Что вы должны знать о jvisualvm
Хотя jvisualvm может контролировать удаленный доступ, он обычно не используется, потому что, если вы хотите контролировать сервер, то настройка порта JMX должна быть запущена при запуске сервера.
Однако открыть такой важный порт в общей производственной среде невозможно, поэтому jvisualvm обычно не используется для мониторинга JVM на линии, поэтому конкретный метод настройки не будет расширен, но среда разработки и тестовая среда могут быть используется по мере необходимости.например испытание под давлением
jstack находит поток с максимальной загрузкой ЦП
Подготовка: я провожу непосредственную демонстрацию для имитации среды разработки, а мы смотрим во время презентации
Используйте команду top, чтобы узнать процесс с самым высоким процессором
Введите top, чтобы просмотреть процессы с наибольшей загрузкой ЦП.
Согласно рисунку, процесс Java, который занимает больше всего ЦП, равен 18955.
Используйте команду top -p идентификатора процесса
Используйте команду top -p номер процесса для просмотраИспользование процессов памятиМы входимtop -p 18955Приди и посмотри
Нажмите H, чтобы ввести подробности процесса
Нажмите H, чтобы просмотреть использование памяти каждым потоком в процессе.
Крайний левый PID — это идентификатор нашего потока.
Найдите код, соответствующий идентификатору потока, по команде jstack
-
Сначала преобразуйте идентификатор потока в шестнадцатеричныйПоскольку идентификатор потока, напечатанный в информации jstack, представлен в шестнадцатеричном формате., так поставь24091 преобразуется в шестнадцатеричное число 5e1b.
-
воплощать в жизньjstack 18955|grep -A 10 5e1b, сопоставьте следующие 10 строк кода в строке, где находится этот поток, и из стека можно найти вызывающий метод, вызывающий парение ЦП
-
Проанализируйте код, напечатанный в стеке, как показано на изображении ниже.Это просто пример, неважно, что в нем
"http-nio-8080-ClientPoller" #43 daemon prio=5 os_prio=31 cpu=24.76ms elapsed=327.74s tid=0x00007fd3f33d0800 nid=0x5e1b runnable [0x000070000d58d000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.KQueue.poll(java.base@11.0.11/Native Method)
at sun.nio.ch.KQueueSelectorImpl.doSelect(java.base@11.0.11/KQueueSelectorImpl.java:122)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.11/SelectorImpl.java:124)
- locked <0x00000007843563e8> (a sun.nio.ch.Util$2)
- locked <0x0000000784356290> (a sun.nio.ch.KQueueSelectorImpl)
at sun.nio.ch.SelectorImpl.select(java.base@11.0.11/SelectorImpl.java:136)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:816)
at java.lang.Thread.run(java.base@11.0.11/Thread.java:834)
jinfo ---> Инструмент информации о конфигурации Java
jinfoConfiguration Info for JavaФункция заключается в просмотре и настройке различных параметров конфигурации JVM в режиме реального времени.
Просмотр параметров JVM
jinfo -flags Идентификатор процесса
Советы:Существует проблема с версией этой команды для jdk8. Она аналогична случаю, когда команда jmap выше не работает. Вам необходимо переключить другие версии jdk.
Просмотр параметров системы
jinfo также может использовать параметр -sysprops для вывода содержимого System.getProperties() процесса виртуальной машины, как показано на следующем рисунке.Пока все знают, что есть эта команда, она редко используется
VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=3 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=4 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:+ManagementServer -XX:MarkStackSize=4194304 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=1287651328 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=6973028 -XX:NonProfiledCodeHeapSize=244685212 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC
zhouxinzedeMacBook-Pro:~ zhouxinze$ jinfo -sysprops 84587
Java System Properties:
#Thu Apr 29 21:55:19 CST 2021
gopherProxySet=false
awt.toolkit=sun.lwawt.macosx.LWCToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=UTF-8
java.class.path=/Users/zhouxinze/IdeaProjects/mall/target/classes\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.4.5/spring-boot-starter-web-2.4.5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter/2.4.5/spring-boot-starter-2.4.5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot/2.4.5/spring-boot-2.4.5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4.5/spring-boot-autoconfigure-2.4.5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4.5/spring-boot-starter-logging-2.4.5.jar\:/Users/zhouxinze/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar\:/Users/zhouxinze/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar\:/Users/zhouxinze/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar\:/Users/zhouxinze/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar\:/Users/zhouxinze/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar\:/Users/zhouxinze/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar\:/Users/zhouxinze/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4.5/spring-boot-starter-json-2.4.5.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.4/jackson-databind-2.11.4.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.4/jackson-annotations-2.11.4.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.4/jackson-core-2.11.4.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.4/jackson-datatype-jdk8-2.11.4.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.4/jackson-datatype-jsr310-2.11.4.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.4/jackson-module-parameter-names-2.11.4.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4.5/spring-boot-starter-tomcat-2.4.5.jar\:/Users/zhouxinze/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.45/tomcat-embed-core-9.0.45.jar\:/Users/zhouxinze/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar\:/Users/zhouxinze/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.45/tomcat-embed-websocket-9.0.45.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-web/5.3.6/spring-web-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-beans/5.3.6/spring-beans-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-webmvc/5.3.6/spring-webmvc-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-aop/5.3.6/spring-aop-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-context/5.3.6/spring-context-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-expression/5.3.6/spring-expression-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-core/5.3.6/spring-core-5.3.6.jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-jcl/5.3.6/spring-jcl-5.3.6.jar
java.vm.vendor=Oracle Corporation
sun.arch.data.model=64
catalina.useNaming=false
java.vendor.url=https\://openjdk.java.net/
user.timezone=Asia/Shanghai
java.vm.specification.version=11
os.name=Mac OS X
sun.java.launcher=SUN_STANDARD
user.country=CN
sun.boot.library.path=/Library/Java/JavaVirtualMachines/jdk-11.0.11.jdk/Contents/Home/lib
spring.application.admin.enabled=true
sun.java.command=com.mall.MallApplication
com.sun.management.jmxremote=
jdk.debug=release
sun.cpu.endian=little
spring.liveBeansView.mbeanDomain=
user.home=/Users/zhouxinze
user.language=zh
java.specification.vendor=Oracle Corporation
java.version.date=2021-04-20
java.home=/Library/Java/JavaVirtualMachines/jdk-11.0.11.jdk/Contents/Home
file.separator=/
spring.output.ansi.enabled=always
java.vm.compressedOopsMode=Zero based
line.separator=\n
java.specification.name=Java Platform API Specification
java.vm.specification.vendor=Oracle Corporation
FILE_LOG_CHARSET=UTF-8
java.awt.graphicsenv=sun.awt.CGraphicsEnvironment
java.awt.headless=true
user.script=Hans
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
java.runtime.version=11.0.11+9-LTS-194
spring.jmx.enabled=true
path.separator=\:
os.version=10.15.6
java.runtime.name=Java(TM) SE Runtime Environment
file.encoding=UTF-8
spring.beaninfo.ignore=true
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.vendor.version=18.9
java.vendor.url.bug=https\://bugreport.java.com/bugreport/
java.io.tmpdir=/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/
catalina.home=/private/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/tomcat.8080.5214054096261208489
java.version=11.0.11
os.arch=x86_64
java.vm.specification.name=Java Virtual Machine Specification
PID=84587
java.awt.printerjob=sun.lwawt.macosx.CPrinterJob
sun.os.patch.level=unknown
CONSOLE_LOG_CHARSET=UTF-8
catalina.base=/private/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/tomcat.8080.5214054096261208489
java.vendor=Oracle Corporation
java.vm.info=mixed mode
java.vm.version=11.0.11+9-LTS-194
java.rmi.server.randomIDs=true
sun.io.unicode.encoding=UnicodeBig
java.class.version=55.0
Фокус: jstat ---> инструмент мониторинга статистики JVM
jstatJVM Statistics Monitoring ToolЭто инструмент командной строки для мониторинга различной информации о рабочем состоянии JVM.Эта команда очень важна.
Это может отображать локальные или удаленные процессы JVMДанные времени выполнения, такие как загрузка классов, память, сборка мусора, своевременная компиляция и т. д., при отсутствии графического интерфейса GUI, предоставляет толькоНа сервере в простой текстовой среде консоли, это будет обычный инструмент для обнаружения проблем с производительностью виртуальной машины во время выполнения.
формат команды jstat
jstat [опция команды] [vmid] [интервал] [количество запросов]
Список опций команды
Первая функция: статистика сборки мусора
jstat -gc идентификатор процесса
- S0C: Размер текущей областиSurvivor0
- S1C: Размер текущей области Survivor1
- S0U: Используемый размер оставшейся в живых0 области
- S1U: регионSurvival1 использовал размер
- EC: размер области Эдема
- ЕС: использовать размер области Эдема
- OC: размер старого поколения
- MC: размер метапространства
- MU: Размер используемого метапространства
- CCSC: размер пространства сжатия указателя
- CCSU: размер используемого пространства сжатия указателя.
- YGC: количество случаев несовершеннолетнего GC с момента запуска программы.
- YGCT: Незначительное время потребления GCПо делу видно, что 4 MinorGC занимают в общей сложности 0,021 секунды.
- FGC: общее количество раз, когда выполнялась полная сборка мусора с момента запуска программы.
- FGCT: общее время, затраченное на полный сборщик мусора
- CGC: параллельный сбор G1Mixed GCчастота
- CGCT: параллельный сбор G1Mixed GCпотраченное время
- GCT: общее время, затраченное на сборку мусора.
Вторая функция: статистика кучи памяти
jstat -gccapacity идентификатор процесса
- NGCMN: Минимальная мощность для молодого поколения
- NGCMX: Максимальная емкость для молодого поколения
- NGC: Текущая мощность молодой генерации
- S0C, S1C, EC и вышеуказанная информация GC
- OGCMN: минимальная емкость старого поколения
- OGCMX: максимальная емкость пожилого возраста
- OGC: текущий размер старого поколения
- OC: то же, что и в информации GC выше
- MCMN: минимальная емкость метапространства
- MCMX: максимальная вместимость в юанях
- MC: текущая емкость метапространства
- CCSMN: минимальный размер пространства сжатого указателя.
- CCSMX: максимальный размер пространства сжатого указателя.
- CCSC: текущий размер пространства сжатого указателя
- YGC, FGC, CGC такие же, как в приведенной выше информации GC
И так далее, если вы хотите просмотреть специальное содержимое мониторинга, просто сделайте следующее изображение,Примечание. Разные сборщики мусора могут печатать разное содержимое., Не один за другим, чтобы принести всех, чтобы увидеть
Оценка рабочего состояния JVM
Помним ли мы интервал времени, упомянутый выше, мы можем использовать этот интервал времени для оценки работы JVM
Например: если вы хотите выполнять статистику jstat gc каждую 1 секунду, всего 10 статистик, мы можем сделать этоjstat -gc 85164 1000 10, мы можем получить статистику на рисунке нижеTomcat был запущен и хранится там, поэтому никаких изменений.Когда вы пишете тестовые классы, вы можете отслеживать изменения данных по постоянно новым объектам.
Что мы можем видеть с этой картины?
- Прогнозирование темпов роста объектов молодого поколения
- Частота триггеров второстепенного GC и среднее времяСреднее затраченное время = YGCT/YGC, общее время MinorGC рассчитывается путем деления времени MinorGC, чтобы получить продолжительность системного интервала.Незначительная частота GCКак долго будет паузаMINOR GC отнимает много времени
- Сколько объектов вступает в старость после каждого Minor GC, наблюдайте за изменениями EU, S0U, S1U и OU после каждого Minor GC, чтобы сделать вывод, сколько объектов вступает в старость в каждом Minor GC, а затем объединяйте Minor GC. частота ГХОпределение скорости роста объектов в пожилом возрасте
- Полная частота запуска GC и среднее времяFGCT/FGC
Этап 2: Общие идеи оптимизации JVM
комбинироватьПравила перемещения предметов в старостиВ основном следующие идеи оптимизации:
-
Проще говоря, постарайтесь сделать каждыйMinor GCОставшиеся уцелевшие объекты составляют менее 50% площади Выжившего.Избегайте преждевременного вступления в старость благодаря динамическому механизму суждения о возрасте., постарайтесь выжить в молодом поколении, попробуйте уменьшить частоту Full GC, Full GC серьезно влияет на производительность JVMЭта ситуация возникает в системе с высокой степенью параллелизма. В нормальных условиях создается очень мало объектов в секунду. Однако в определенный период времени объем параллелизма внезапно увеличивается, что приводит к слишком быстрому созданию новых объектов, что легко вызван механизмом динамического суждения о возрасте.Войдите в старое поколение преждевременно, поэтому в этом случае отрегулируйте размер молодого поколения, чтобы как можно больше сохранить эти объекты в молодом поколении, потому что это все объекты, которые умирают в мгновение ока
-
большой объектОбъекты, которые требуют большого количества смежных пространств памяти, такие как строки, массивыВойдите в старое поколение как можно раньше, потому что молодое поколение используеталгоритм пометки-копии, большой объект находится вкопироватьЭто будет потреблять много производительности, поэтому войдите в старость как можно скорее.Используйте -XX:PretenureSizeThreshold, чтобы задать размер. Объекты, превышающие этот размер, напрямую попадают в старое поколение и не попадают в молодое поколение. Этот параметр полезен только для сборщиков Serial и ParNew GC.
КомбинацияПолный GC возникаетКоличество раз, в основном за счет следующих идей оптимизации
-
За исключением нормальногоПолный сборщик мусора происходит из-за нехватки места в старом поколении,иМеханизм гарантированного пребывания в старостиналичие молодого поколения в каждомДо несовершеннолетнего ГХ, JVM рассчитаетК старости осталось свободное место, если это свободное пространствоменьше суммы размеров всех существующих объектов в молодом поколении, увидите-XX:-HandlePromotionFailure JDK 1.8 по умолчаниюНезависимо от того, установлен ли параметр, этот параметр является параметром гарантии, гарантия, еслиОставшееся пространство в старом поколении меньше среднего значения объектов, попадающих в старое поколение после каждого Minor GC в истории., сначала будет выполняться Full GC, а затем будет выполняться Minor GC.После Minor GC, если старости будет недостаточно, снова произойдет Full GC.Полный Minor GC — это два Full GC и один Minor GC.
-
Недостаточное метапространство может привести кЧрезмерный полный GC, что приводит к частому полному сбору мусора
-
Это показывает, что вызов System.gc вызывает избыточный полный сборщик мусора.-XX:+DisableExplicitGCПараметр отключен, и с этим параметром System.gc не действует
Этап 3: Варианты настройки JVM
Пример настройки JVM: частый полный сборщик мусора онлайн
Предположим, есть такая строка параметров JVMСмоделируйте среду JVM в реальном сценарии
-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
Для учащихся, которые не уверены в этих командах, см. следующее введение в инструкции.Если вы знаете, вы можете пропустить следующее содержимое:
- -Xms1536M: установите начальную память JVM на 1536M. Это значение можно установить так же, как -Xmx, чтобы избежать перераспределения памяти JVM после завершения каждой сборки мусора.
- -Xmx1536M: установите максимально доступную память JVM на 1536M.
- -Xmn512M: установить размер молодого поколения на 512M
- -Xss256K: установить размер стека каждого потока на 256 КБ.
- -XX:SurvivorRatio=6: установить соотношение размеров области Эдема и области выживших в молодом поколении. Если установлено значение 6, соотношение двух областей Выживших к одной области Эдема составляет 2:6, а на одну область Выживших приходится 1/8 всего молодого поколения.
- -XX:MetaspaceSize=256M: установить размер метапространства на 256M.
- -XX:MaxMetaspaceSize=256M: установить максимальный размер метапространства на 256M.
- -XX:+UseParNewGC: установите для сборщика мусора молодого поколения значение ParNew.
- -XX:+UseConcMarkSweepGC: установить сборщик мусора старого поколения как CMS.
- -XX:CMSInitiatingOccupancyFraction=75: установить CMS для запуска GC, когда использование памяти старого поколения достигает 75%Поскольку в CMS будет плавающий мусор, GC обычно запускается раньше.
- -XX:+UseCMSInitiatingOccupancyOnly: использовать только установленный порог восстановления75% указано выше, если не указано, JVMУставка используется только в первый раз, он будет автоматически скорректирован позже, обычно используется в сочетании с предыдущей командой
Гипотеза 1: Полный GC часто возникает из-за динамического механизма суждения о возрасте.
тогда потому чтоПричины механизма динамического суждения о возрастеВ результате частое появление полного GC, как его настроить, давайте сначала рассмотрим следующие аспекты?
-
Механизм динамического возрастаКлючевым моментом является размер пространства молодого поколения, поэтому в первую очередь необходимо увеличить пространство молодого поколения.
-
Если это система с большим количеством параллелизма, мы можем уменьшить его.CMSInitiatingOccupancyFractionустановить значение, чтобы избежать генерацииДело коллекционера Serial Old, но если это система с небольшим параллелизмом, мы можем увеличить егоCMSInitiatingOccupancyFractionУстановите значение, чтобы полностью использовать пространство кучи
Поэтому после настройки параметры JVM выглядят так, как показано в следующем коде:Увеличьте пространство молодого поколения до 1024 МБ, чтобы пространство старого поколения было 512 МБ. Увеличьте порог использования памяти CMS старого поколения до 90%, чтобы полностью использовать пространство старого поколения. большое количество параллелизма, учащимся может потребоваться уменьшить это значение, чтобы избежать параллелизма Конфликт приводит к использованию сборщика Serial Old
-Xms1536M -Xmx1536M -Xmn1024M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=90 -XX:+UseCMSInitiatingOccupancyOnly
Гипотеза 2: Полная сборка мусора происходит часто из-за механизма гарантии пространства старого поколения.
Если вы установите старость как маленькую в соответствии с вышеуказанными настройками, легко вызвать проблемы из-заМеханизм гарантированного пребывания в старости, что приводит к частому возникновению полного GC,Механизм гарантированного пребывания в старостиКлючевым моментом является то, что средний размер объектов, вступающих в старость каждый раз, когда Minor GC, поэтому нам нужно контролироватьСредний размер объектов, попадающих в старое поколение после каждого Minor GC
Определить распределение объектов в памяти
использоватьjmap - гисто идентификатор процессакоманды, понаблюдайте за распределением объектов в памяти, и понаблюдайте, есть ли относительно сконцентрированные объекты, потому что если это система с высоким параллелизмом, то скорее всего интерфейс будет централизованным, и создаваемые объекты тоже централизованы, так что можно начинать отСпособ использования CPU, метод, который горячий,Объекты, которые занимают много памятиПроанализируйте эти два аспекта
- Поиск горячих точек с помощью сэмплера jvisualvmjdk8 имеет jdk11 не поддерживает
- Используйте номер процесса jmap -histo для наблюдения за объектами, которые занимают много памяти.Начните с объектов в вашем проекте
Направление оптимизации
-
Если объект создается в цикле, попробуйте контролировать количество цикловНапример, если каждый раз запрашивается 5000 записей, если эти записи загружены в память, будет создано много объектов.Если эти объекты проходят через Minor GC, легко вызвать Full GC из-за механизма гарантии выделения пространства старость, поэтому необходимо уменьшить количество записей запроса, тем самым уменьшив количество создаваемых объектов
-
Самый быстрый и эффективный способ, если позволяет бюджет,Увеличьте конфигурацию физической машины, увеличение памяти всей кучи, если позволяют условия, тоже хороший метод
Резюме этой статьи
Что ж, вышеизложенное - это все содержание этой статьи, в этой статье мы также прошли следующие шаги.
- Этап 1: рассказБазовое использование команд jmap, jinfo, jstack, jstat, и какой эффект
- Второй этап: описывает направление идей оптимизации JVM, которое можно разделить на два направления
- Третий этап: поПолная сборка мусора часто происходит онлайнВ этом случае описывается, как различные команды используются вместе.
Команд много.Можно собрать их много.Когда столкнешься с проблемой в следующий раз,можешь сразу перевернуть и использовать.
болтовня
Наконец, если вы чувствуете какую-либо путаницу в статье, пожалуйста, оставьте комментарий как можно скорее.маленький новичокЕсли у меня что-то есть, ставьте лайк 👍 Подписывайтесь ❤️ Делитесь 👥 Мне это очень полезно! ! !