简体   繁体   English

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

[英]Java - Get a list of all Classes loaded in the JVM

I would like to get a list of all the classes belonging to a certain package as well as all of their children.我想获得属于某个包的所有类及其所有子类的列表。 The classes may or may not be already loaded in the JVM.这些类可能已经或可能尚未加载到 JVM 中。

It's not a programmatic solution but you can run这不是程序化解决方案,但您可以运行

java -verbose:class ....

and the JVM will dump out what it's loading, and from where. 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]

See here for more details.有关更多详细信息,请参见此处

using the Reflections library, it's easy as:使用Reflections库,很简单:

Reflections reflections = new Reflections("my.pkg", new SubTypesScanner(false));

That would scan all classes in the url/s that contains my.pkg package.这将扫描包含 my.pkg 包的 url/s 中的所有类。

  • the false parameter means - don't exclude the Object class, which is excluded by default. false 参数意味着 - 不要排除默认情况下排除的 Object 类。
  • in some scenarios (different containers) you might pass the classLoader as well as a parameter.在某些情况下(不同的容器),您可能会传递 classLoader 以及参数。

So, getting all classes is effectively getting all subtypes of Object, transitively:因此,获取所有类实际上是获取 Object 的所有子类型,可传递:

Set<String> allClasses = 
    reflections.getStore().getSubTypesOf(Object.class.getName());

(The ordinary way reflections.getSubTypesOf(Object.class) would cause loading all classes into PermGen and would probably throw OutOfMemoryError. you don't want to do it...) reflections.getSubTypesOf(Object.class)的普通方式会导致将所有类加载到 PermGen 中,并且可能会抛出 OutOfMemoryError。你不想这样做......)

If you want to get all direct subtypes of Object (or any other type), without getting its transitive subtypes all in once, use this:如果您想获得 Object (或任何其他类型)的所有直接子类型,而不是一次性获得其传递子类型,请使用以下命令:

Collection<String> directSubtypes = 
    reflections.getStore().get(SubTypesScanner.class).get(Object.class.getName());

There are multiple answers to this question, partly due to ambiguous question - the title is talking about classes loaded by the JVM, whereas the contents of the question says "may or may not be loaded by the JVM".这个问题有多个答案,部分原因是模棱两可的问题 - 标题是关于由 JVM 加载的类,而问题的内容是“可能由 JVM 加载,也可能不被 JVM 加载”。

Assuming that OP needs classes that are loaded by the JVM by a given classloader, and only those classes - my need as well - there is a solution ( elaborated here ) that goes like this:假设 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();
        }
    }

}

One of the neat things about it is that you can choose an arbitrary classloader you want to check.它的优点之一是您可以选择要检查的任意类加载器。 It is however likely to break should internals of classloader class change, so it is to be used as one-off diagnostic tool.然而,如果类加载器类的内部发生变化,它可能会中断,因此它被用作一次性诊断工具。

I'd also suggest you write a -javagent agent, but use the getAllLoadedClasses method instead of transforming any classes.我还建议您编写一个-javagent代理,但使用getAllLoadedClasses方法而不是转换任何类。

To synchronize with your client code (Normal Java code), create a socket and communicate with the agent through it.要与您的客户端代码(普通 Java 代码)同步,请创建一个套接字并通过它与代理通信。 Then you can trigger a "list all classes" method whenever you need.然后,您可以在需要时触发“列出所有类”方法。

An alternative approach to those described above would be to create an external agent using java.lang.instrument to find out what classes are loaded and run your program with the -javaagent switch:上述方法的另一种方法是使用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;
    }
}

This approach has the added benefit of providing you with information about which ClassLoader loaded a given class.这种方法的另一个好处是为您提供有关哪个 ClassLoader 加载了给定类的信息。

List of all Classes loaded in the JVM JVM中加载的所有类的列表

From Oracle doc you can use -Xlog option that has a possibility to write into file.Oracle 文档中,您可以使用-Xlog选项,该选项可以写入文件。

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

One way if you already know the package top level path is to use OpenPojo如果您已经知道包顶级路径,一种方法是使用OpenPojo

final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);

Then you can go over the list and perform any functionality you desire.然后您可以浏览列表并执行您想要的任何功能。

This program will prints all the classes with its physical path.该程序将打印所有类及其物理路径。 use can simply copy this to any JSP if you need to analyse the class loading from any web/application server.如果您需要分析从任何 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();
        }
    }
}

Run your code under a JRockit JVM, then use JRCMD <PID> print_class_summary在 JRockit JVM 下运行您的代码,然后使用JRCMD <PID> print_class_summary

This will output all loaded classes, one on each line.这将输出所有加载的类,每行一个。

You might be able to get a list of classes that are loaded through the classloader but this would not include classes you haven't loaded yet but are on your classpath.您可能能够获得通过类加载器加载的类的列表,但这不包括您尚未加载但在类路径中的类。

To get ALL classes on your classpath you have to do something like your second solution.要在类路径上获取所有类,您必须执行类似于第二个解决方案的操作。 If you really want classes that are currently "Loaded" (in other words, classes you have already referenced, accessed or instantiated) then you should refine your question to indicate this.如果您真的想要当前“加载”的类(换句话说,您已经引用、访问或实例化的类),那么您应该细化您的问题以表明这一点。

There's another possibility using VM.class_hierarchy , available since JDK 8 (tested on 1.8.0_322).使用VM.class_hierarchy的另一种可能性,自 JDK 8 起可用(在 1.8.0_322 上测试)。

$ jcmd 44506 VM.class_hierarchy

This will give a result like that :这将给出这样的结果:

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
...

If you don't want any libraries and need this information given to you at runtime, you can use this for Java 11+.如果您不想要任何库并且需要在运行时向您提供此信息,您可以将其用于 Java 11+。 It finds all system modules loaded at runtime, iterates over their entries (path names) and collects class items.它查找在运行时加载的所有系统模块,遍历它们的条目(路径名)并收集类项。

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());
}

If you need non-system classes then there are some conditions to consider.如果您需要非系统类,则需要考虑一些条件。 In both situations you can use a class reference to something in your project to get the needed context to find other classes.在这两种情况下,您都可以使用对项目中某些内容的class引用来获取所需的上下文以查找其他类。

If you want only the currently loaded classes, you can use reflection to access the Vector<Class<?>> classes in ClassLoader .如果您只想要当前加载的类,您可以使用反射来访问ClassLoader中的Vector<Class<?>> classes This will not include classes from libraries and such that have not yet been initialized.这将不包括库中的类和尚未初始化的类。

仅初始化类的屏幕截图

If you want all classes of all libraries you have in a project then you'll want to use reflection to access the AppClassLoader 's URLClassPath ucp .如果您想要项目中所有库的所有类,那么您将需要使用反射来访问AppClassLoaderURLClassPath ucp This will hold a ArrayList<URL> path containing URL's pointing to every directory/jar/etc holding referenced resources.这将包含一个ArrayList<URL> path ,其中包含指向每个包含引用资源的目录/jar/etc 的 URL。 Navigating those you can use path-walking to collect the names of class entries similar to the code snippet above.导航那些你可以使用路径行走来收集类条目的名称,类似于上面的代码片段。

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

Well, what I did was simply listing all the files in the classpath.好吧,我所做的只是列出类路径中的所有文件。 It may not be a glorious solution, but it works reliably and gives me everything I want, and more.它可能不是一个光荣的解决方案,但它可靠地工作并为我提供了我想要的一切,甚至更多。

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

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