繁体   English   中英

Java - 获取 JVM 中加载的所有类的列表

[英]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 中的所有类。

  • false 参数意味着 - 不要排除默认情况下排除的 Object 类。
  • 在某些情况下(不同的容器),您可能会传递 classLoader 以及参数。

因此,获取所有类实际上是获取 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 加载了给定类的信息。

JVM中加载的所有类的列表

Oracle 文档中,您可以使用-Xlog选项,该选项可以写入文件。

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

如果您已经知道包顶级路径,一种方法是使用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 这将不包括库中的类和尚未初始化的类。

仅初始化类的屏幕截图

如果您想要项目中所有库的所有类,那么您将需要使用反射来访问AppClassLoaderURLClassPath ucp 这将包含一个ArrayList<URL> path ,其中包含指向每个包含引用资源的目录/jar/etc 的 URL。 导航那些你可以使用路径行走来收集类条目的名称,类似于上面的代码片段。

引用库的文件路径 uri 的屏幕截图

好吧,我所做的只是列出类路径中的所有文件。 它可能不是一个光荣的解决方案,但它可靠地工作并为我提供了我想要的一切,甚至更多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM