[英]Java - Get a list of all Classes loaded in the JVM
我想获得属于某个包的所有类及其所有子类的列表。 这些类可能已经或可能尚未加载到 JVM 中。
这不是程序化解决方案,但您可以运行
java -verbose:class ....
JVM 将转储它正在加载的内容以及从何处加载。
[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
有关更多详细信息,请参见此处。
使用Reflections库,很简单:
Reflections reflections = new Reflections("my.pkg", new SubTypesScanner(false));
这将扫描包含 my.pkg 包的 url/s 中的所有类。
因此,获取所有类实际上是获取 Object 的所有子类型,可传递:
Set<String> allClasses =
reflections.getStore().getSubTypesOf(Object.class.getName());
( reflections.getSubTypesOf(Object.class)
的普通方式会导致将所有类加载到 PermGen 中,并且可能会抛出 OutOfMemoryError。你不想这样做......)
如果您想获得 Object (或任何其他类型)的所有直接子类型,而不是一次性获得其传递子类型,请使用以下命令:
Collection<String> directSubtypes =
reflections.getStore().get(SubTypesScanner.class).get(Object.class.getName());
这个问题有多个答案,部分原因是模棱两可的问题 - 标题是关于由 JVM 加载的类,而问题的内容是“可能由 JVM 加载,也可能不被 JVM 加载”。
假设 OP 需要由给定类加载器由 JVM 加载的类,并且只有那些类 - 我也需要 - 有一个解决方案( 在此处详细说明)如下所示:
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
public class CPTest {
private static Iterator list(ClassLoader CL)
throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Class CL_class = CL.getClass();
while (CL_class != java.lang.ClassLoader.class) {
CL_class = CL_class.getSuperclass();
}
java.lang.reflect.Field ClassLoader_classes_field = CL_class
.getDeclaredField("classes");
ClassLoader_classes_field.setAccessible(true);
Vector classes = (Vector) ClassLoader_classes_field.get(CL);
return classes.iterator();
}
public static void main(String args[]) throws Exception {
ClassLoader myCL = Thread.currentThread().getContextClassLoader();
while (myCL != null) {
System.out.println("ClassLoader: " + myCL);
for (Iterator iter = list(myCL); iter.hasNext();) {
System.out.println("\t" + iter.next());
}
myCL = myCL.getParent();
}
}
}
它的优点之一是您可以选择要检查的任意类加载器。 然而,如果类加载器类的内部发生变化,它可能会中断,因此它被用作一次性诊断工具。
我还建议您编写一个-javagent
代理,但使用getAllLoadedClasses方法而不是转换任何类。
要与您的客户端代码(普通 Java 代码)同步,请创建一个套接字并通过它与代理通信。 然后,您可以在需要时触发“列出所有类”方法。
上述方法的另一种方法是使用java.lang.instrument
创建一个外部代理,以找出加载了哪些类并使用-javaagent
开关运行您的程序:
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class SimpleTransformer implements ClassFileTransformer {
public SimpleTransformer() {
super();
}
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
System.out.println("Loading class: " + className);
return bytes;
}
}
这种方法的另一个好处是为您提供有关哪个 ClassLoader 加载了给定类的信息。
如果您已经知道包顶级路径,一种方法是使用OpenPojo
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
然后您可以浏览列表并执行您想要的任何功能。
该程序将打印所有类及其物理路径。 如果您需要分析从任何 Web/应用程序服务器加载的类,use 可以简单地将其复制到任何 JSP。
import java.lang.reflect.Field;
import java.util.Vector;
public class TestMain {
public static void main(String[] args) {
Field f;
try {
f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes = (Vector<Class>) f.get(classLoader);
for(Class cls : classes){
java.net.URL location = cls.getResource('/' + cls.getName().replace('.',
'/') + ".class");
System.out.println("<p>"+location +"<p/>");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在 JRockit JVM 下运行您的代码,然后使用JRCMD <PID> print_class_summary
这将输出所有加载的类,每行一个。
您可能能够获得通过类加载器加载的类的列表,但这不包括您尚未加载但在类路径中的类。
要在类路径上获取所有类,您必须执行类似于第二个解决方案的操作。 如果您真的想要当前“加载”的类(换句话说,您已经引用、访问或实例化的类),那么您应该细化您的问题以表明这一点。
使用VM.class_hierarchy
的另一种可能性,自 JDK 8 起可用(在 1.8.0_322 上测试)。
$ jcmd 44506 VM.class_hierarchy
这将给出这样的结果:
44506:
java.lang.Object/null
|--com.intellij.psi.impl.source.tree.JavaElementType$$Lambda$1163/0x0000000800cd86d8/0x0000600002f012c0
|--com.intellij.ide.IdeTooltipManager/0x0000600002f2c6e0
|--sun.security.ssl.SSLBasicKeyDerivation$SecretSizeSpec/null
|--com.intellij.openapi.editor.impl.view.EditorSizeManager$$Lambda$2094/0x0000000801774c38/0x0000600002f2c6e0
|--com.intellij.psi.util.CachedValueProfiler$EventPlace/0x0000600002f2c6e0 (intf)
|--io.ktor.utils.io.internal.ReadWriteBufferStateKt/0x0000600002fcd680
|--com.intellij.javascript.nodejs.library.core.codeInsight.NodePredefinedReferenceErrorUpdater/0x0000600002f13660
|--com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl$MyAsyncFileListener/0x0000600002f2c6e0
|--java.lang.management.ManagementFactory$PlatformMBeanFinder$$Lambda$89/0x000000080013e0b0/null
|--org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider$AvailabilityInfo/0x0000600002f0ada0
| |--org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider$AvailabilityInfo$2/0x0000600002f0ada0
| |--org.intellij.plugins.markdown.ui.preview.MarkdownHtmlPanelProvider$AvailabilityInfo$1/0x0000600002f0ada0
|--java.lang.invoke.LambdaForm$DMH/0x000000080012a800/null
|--git4idea.status.GitStagingAreaHolder$$Lambda$2907/0x0000000801d2e690/0x0000600002f41cc0
|--com.intellij.lang.javascript.refactoring.extractSuper.JSCustomExtractInterfaceHandler/0x0000600002f13660 (intf)
|--javax.xml.transform.stream.StreamSource/null
...
如果您不想要任何库并且需要在运行时向您提供此信息,您可以将其用于 Java 11+。 它查找在运行时加载的所有系统模块,遍历它们的条目(路径名)并收集类项。
public static List<String> getSystemClasses() {
// Errorables is a util class to ignore exceptions on lambda types, easy enough to implement yourself
return ModuleFinder.ofSystem().findAll().stream()
.map(modRef -> Errorables.silent(modRef::open)) // open reader to each module
.flatMap(modReader -> Errorables.silent(modReader::list)) // list all items in the module
.filter(s -> s.endsWith(".class") && s.indexOf('-') == -1) // retain only classes (except module-info or package-info)
.map(s -> s.substring(0, s.length() - 6)) // cut off '.class' from the path
.collect(Collectors.toList());
}
如果您需要非系统类,则需要考虑一些条件。 在这两种情况下,您都可以使用对项目中某些内容的class
引用来获取所需的上下文以查找其他类。
如果您只想要当前加载的类,您可以使用反射来访问ClassLoader
中的Vector<Class<?>> classes
。 这将不包括库中的类和尚未初始化的类。
如果您想要项目中所有库的所有类,那么您将需要使用反射来访问AppClassLoader
的URLClassPath ucp
。 这将包含一个ArrayList<URL> path
,其中包含指向每个包含引用资源的目录/jar/etc 的 URL。 导航那些你可以使用路径行走来收集类条目的名称,类似于上面的代码片段。
好吧,我所做的只是列出类路径中的所有文件。 它可能不是一个光荣的解决方案,但它可靠地工作并为我提供了我想要的一切,甚至更多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.