Перейти к основному содержимому

Найдите алгоритм GC, используемый экземпляром JVM

· 5 мин. чтения

1. Обзор

В дополнение к типичным утилитам разработки, таким как компилятор и среда выполнения, каждый выпуск JDK поставляется с множеством других инструментов. Некоторые из этих инструментов могут помочь нам получить ценную информацию о наших запущенных приложениях.

В этой статье мы увидим, как мы можем использовать такие инструменты, чтобы узнать больше об алгоритме GC, используемом конкретным экземпляром JVM.

2. Образец заявления

В этой статье мы будем использовать очень простое приложение:

public class App {
public static void main(String[] args) throws IOException {
System.out.println("Waiting for stdin");
int read = System.in.read();
System.out.println("I'm done: " + read);
}
}

Очевидно, это приложение ждет и продолжает работать, пока не получит что-то из стандартного ввода. Эта приостановка помогает нам имитировать поведение долго работающих приложений JVM.

Чтобы использовать это приложение, мы должны скомпилировать файл App.java с помощью javac , а затем запустить его с помощью инструмента java .

3. Поиск процесса JVM

Чтобы найти GC, используемый процессом JVM, сначала мы должны определить идентификатор процесса этого конкретного экземпляра JVM. Допустим, мы запустили наше приложение с помощью следующей команды:

>> java App
Waiting for stdin

Если у нас установлен JDK, лучший способ найти идентификатор процесса экземпляров JVM — использовать инструмент jps . Например:

>> jps -l
69569
48347 App
48351 jdk.jcmd/sun.tools.jps.Jps

Как показано выше, в системе запущено три экземпляра JVM. Очевидно, что описание второго экземпляра JVM («Приложение») совпадает с названием нашего приложения. Следовательно, идентификатор процесса, который мы ищем, — 48347.

В дополнение к jps мы всегда можем использовать другие общие утилиты для фильтрации запущенных процессов. Например, известный инструмент ps из пакета procps также будет работать:

>> ps -ef | grep java
502 48347 36213 0 1:28AM ttys037 0:00.28 java App

Однако jps намного проще в использовании и требует меньше фильтрации.

4. Используемый ГХ

Теперь, когда мы знаем, как найти идентификатор процесса, давайте найдем алгоритм GC, используемый уже запущенными приложениями JVM.

4.1. Java 8 и более ранние версии

Если мы используем Java 8, мы можем использовать утилиту jmap для печати сводки кучи, гистограммы кучи или даже создания дампа кучи . Чтобы найти алгоритм GC, мы можем использовать параметр -heap как:

>> jmap -heap <pid>

Итак, в нашем конкретном случае мы используем CMS GC:

>> jmap -heap 48347 | grep GC
Concurrent Mark-Sweep GC

Для других алгоритмов GC вывод почти такой же:

>> jmap -heap 48347 | grep GC
Parallel GC with 8 thread(s)

4.2. Java 9+: jhsdb jmap

Начиная с Java 9, мы можем использовать комбинацию jhsdb jmap для вывода некоторой информации о куче JVM. В частности, эта конкретная команда будет эквивалентна предыдущей:

>> jhsdb jmap --heap --pid <pid>

Например, наше приложение сейчас работает с G1GC:

>> jhsdb jmap --heap --pid 48347 | grep GC
Garbage-First (G1) GC with 8 thread(s)

4.3. Java 9+: jcmd

В современных JVM команда jcmd довольно универсальна. Например, мы можем использовать его для получения общей информации о куче :

>> jcmd <pid> VM.info

Итак, если мы передадим идентификатор процесса нашего приложения, мы увидим, что этот экземпляр JVM использует Serial GC:

>> jcmd 48347 VM.info | grep gc
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, serial gc, bsd-amd64)
// omitted

Вывод аналогичен для G1 или ZGC:

// ZGC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, z gc, bsd-amd64)
// G1GC
# Java VM: OpenJDK 64-Bit Server VM (15+36-1562, mixed mode, sharing, tiered, compressed oops, g1 gc, bsd-amd64)

Применив немного магии grep , мы также можем удалить все эти шумы и просто получить имя сборщика мусора:

>> jcmd 48347 VM.info | grep -ohE "[^\s^,]+\sgc"
g1 gc

4.4. Аргументы командной строки

Иногда мы (или кто-то другой) явно указываем алгоритм GC при запуске приложения JVM. Например, здесь мы решили использовать ZGC:

>> java -XX:+UseZGC App

В таких случаях есть гораздо более простые способы найти используемый GC. По сути, все, что нам нужно сделать, это каким-то образом найти команду, с которой было выполнено приложение .

Например, на платформах на базе UNIX мы можем снова использовать команду ps :

>> ps -p 48347 -o command=
java -XX:+UseZGC App

Из приведенного выше вывода очевидно, что JVM использует ZGC. Точно так же команда jcmd также может распечатать аргументы командной строки :

>> jcmd 48347 VM.flags
84020:
-XX:CICompilerCount=4 -XX:-UseCompressedOops -XX:-UseNUMA -XX:-UseNUMAInterleaving -XX:+UseZGC // omitted

Удивительно, как показано выше, эта команда будет печатать как неявные, так и явные аргументы и настраиваемые параметры . Таким образом, даже если мы не укажем алгоритм GC явно, он покажет выбранный и используемый по умолчанию:

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseG1GC

И что еще более удивительно, это будет работать и на Java 8:

>> jcmd 48347 VM.flags | grep -ohE '\S*GC\s'
-XX:+UseParallelGC

5. Вывод

В этой статье мы рассмотрели различные подходы к поиску алгоритма GC, используемого конкретным экземпляром JVM. Некоторые из упомянутых подходов были привязаны к конкретным версиям Java, а некоторые были переносимыми.

Более того, мы увидели пару способов найти id процесса, который нужен всегда.