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 и воспользуемся интерфейсом гистограммы:
Теперь мы откроем файл дампа кучи heapdump.bin
в интерфейсе Java VisualVM и используем классы по параметрам экземпляров или размера:
2.3. JProfiler
JProfiler — один из лучших профилировщиков приложений Java с богатым набором функций для просмотра различных показателей.
В JProfiler мы можем подключиться к работающей JVM или загрузить файл дампа кучи и получить все метрики, связанные с JVM, включая имена всех загруженных классов.
Мы будем использовать функцию процесса присоединения, чтобы позволить JProfiler подключиться к запущенному приложению ListLoadedClass
:
Затем мы сделаем снимок приложения и используем его для загрузки всех классов:
Ниже мы можем увидеть имена счетчиков экземпляров загруженных классов, использующих функциональность Heap Walker:
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 .