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

Список всех классов, загруженных в JVM

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

1. Обзор

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

Мы рассмотрим как непрограммные, так и программные подходы.

2. Непрограммный подход

2.1. Использование аргумента виртуальной машины

Самый простой способ перечислить все загруженные классы — зарегистрировать это в выводе консоли или в файле.

Мы запустим приложение Java со следующим аргументом JVM:

java <app_name> --verbose:class
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar]
...............................

Для Java 9 мы будем использовать аргумент JVM -Xlog для регистрации классов, загруженных в файл:

java <app_name> -Xlog:class+load=info:classloaded.txt

2.2. Использование дампа кучи

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

jmap -dump:format=b,file=/opt/tmp/heapdump.bin <app_pid>

Приведенный выше дамп кучи можно открыть в различных инструментах для получения разных метрик.

В Eclipse мы загрузим файл дампа кучи heapdump.bin в Eclipse Memory Analyzer и воспользуемся интерфейсом гистограммы:

./f7c42e65923246216ff48fcbfab0724b.png

Теперь мы откроем файл дампа кучи heapdump.bin в интерфейсе Java VisualVM и используем классы по параметрам экземпляров или размера:

./a736aafc8ac412e455e122843e2e27c7.png

2.3. JProfiler

JProfiler — один из лучших профилировщиков приложений Java с богатым набором функций для просмотра различных показателей.

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

Мы будем использовать функцию процесса присоединения, чтобы позволить JProfiler подключиться к запущенному приложению ListLoadedClass :

./9d4a59e1726c30632b3236b119e45f85.png

Затем мы сделаем снимок приложения и используем его для загрузки всех классов:

./e0724f66460ac30bf682867688cb069f.png

Ниже мы можем увидеть имена счетчиков экземпляров загруженных классов, использующих функциональность Heap Walker:

./aba47cbab9dce1ec744fc68b579ed636.png

3. Программный подход

3.1. Инструментальный API

Java предоставляет Instrumentation API , который помогает получать ценные метрики для приложения. Во-первых, нам нужно создать и загрузить агент Java, чтобы получить экземпляр интерфейса Instrumentation в приложении. Агент Java — это инструмент для инструментирования программ, работающих на JVM.

Затем нам нужно будет вызвать инструментальный метод getInitiatedClasses (загрузчик классов) , чтобы получить все классы, загруженные определенным типом загрузчика классов. ``

3.2. Google Гуава

Мы увидим, как библиотека Guava может получить список всех классов, загруженных в JVM, используя текущий загрузчик классов.

Давайте начнем с добавления зависимости Guava в наш проект Maven:

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>

Мы инициализируем объект ClassPath текущим экземпляром загрузчика классов:

ClassPath classPath = ClassPath.from(ListLoadedClass.class.getClassLoader());
Set<ClassInfo> classes = classPath.getAllClasses();
Assertions.assertTrue(4 < classes.size());

3.3. API отражений

Мы будем использовать библиотеку Reflections , которая сканирует текущий путь к классам и позволяет запрашивать его во время выполнения.

Давайте начнем с добавления зависимости отражений в наш проект Maven:

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>

Теперь мы рассмотрим пример кода, который возвращает набор классов в пакете:

Reflections reflections = new Reflections(packageName, new SubTypesScanner(false));
Set<Class> classes = reflections.getSubTypesOf(Object.class)
.stream()
.collect(Collectors.toSet());
Assertions.assertEquals(4, classes.size());

4. Вывод

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

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

Как всегда, весь код доступен на GitHub .