概述
在这个教程中,我们将学习在Java虚拟机(JVM)中列出所有加载的类的不同方法。例如,我们可以加载JVM的堆转储或者将运行的应用程序连接到各种工具,并在该工具中列出加载的所有类。此外,还有多种库可以实现这一点。
我们将探索非编程和编程两种方法。
非编程方法
1. 使用VM参数
列出所有已加载类的最直接方法是在控制台输出或文件中记录。
我们将使用以下JVM参数运行Java应用程序:
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,我们将使用-Xlog
JVM参数将加载到文件中的类信息日志化:
java <app_name> -Xlog:class+load=info:classloaded.txt
2.2. 使用堆转储
我们将了解不同的工具如何使用JVM的堆转储来提取类加载信息。首先,我们使用以下命令生成堆转储:
jmap -dump:format=b,file=/opt/tmp/heapdump.bin <app_pid>
这个堆转储文件可以在各种工具中打开,以获取不同的指标。
在Eclipse中,我们将在Eclipse Memory Analyzer中加载名为heapdump.bin
的堆转储文件,并使用直方图界面:
然后,我们将在Java的VisualVM界面中打开名为heapdump.bin
的堆转储文件,并使用按实例或大小选项查看类:
2.3. JProfiler
JProfiler 是一个顶级的Java应用性能分析器,拥有丰富的功能来查看不同的指标。
在JProfiler中,我们可以附加到正在运行的JVM或加载堆转储文件,并获取所有与JVM相关的指标,包括加载的所有类名。
我们将使用附加进程功能让JProfiler连接到运行的应用程序ListLoadedClass
:
然后,我们将捕获应用程序的快照,并使用它来获取所有加载的类:
下面,我们可以看到使用堆步行功能显示加载类的实例计数:
编程方法
3.1. Instrumentation API
Java提供了Instrumentation API,帮助我们获取应用程序的有价值指标。首先,我们需要创建并加载一个Java代理,以便将Instrumentation
接口的实例引入到应用程序中。Java代理是一种工具,用于对运行在JVM上的程序进行调试。
然后,我们需要调用Instrumentation
方法[getInitiatedClasses(ClassLoader loader)](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/Instrumentation.html#getInitiatedClasses\(java.lang.ClassLoader\))
,以获取由特定类加载器类型加载的所有类。
3.2. Google Guava
我们将了解Guava库如何使用当前类加载器获取JVM中加载的所有类的列表。
首先,让我们在Maven项目中添加Guava依赖项:
<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项目中添加reflections
依赖:
<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());
结论
在这篇文章中,我们学习了在JVM中列出所有加载类的各种方式。首先,我们了解了如何使用VM参数来记录已加载类的列表。
接着,我们探讨了各种工具如何加载堆转储或连接到JVM,以显示包括已加载类在内的各种指标。最后,我们涵盖了部分Java库。
如往常一样,所有的代码都可在GitHub上找到。