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


70

我想获得属于某个包的所有类及其所有子级的列表。这些类可能已经或可能尚未加载到JVM中。

Answers:


112

这不是编程解决方案,但您可以运行

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]

有关更多详细信息,请参见此处


1
这些问题询问“获取在JVM中加载的所有类的列表”,而不是“ .jar”加载的列表。
Alessandro C,

1
上面的输出详细说明了打开的罐子以及随后的类加载
Brian Agnew

对,在第一刻,我只读了最右端的“ .jar”……我的错。抱歉!
Alessandro C

这是某种黑客吗?用户配置会调整此详细级别吗?
Sajuuk

如果每个java进程有一个JVM,即在一台机器上可以运行许多JVM(java进程),那么您的命令输出对应哪个JVM?
Sajuuk

30

使用反射库,很容易做到:

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

您正在使用什么Reflections lib版本?本示例不适用于Reflections 0.9.5。
pablosaraiva 2014年

1
没关系,只是使用反射-0.9.9-RC1-uberjar
pablosaraiva,2014年

3
您只需经历将另一个库添加到项目中的痛苦。在Java中使用该语言时,不允许使用“简单”一词。您给我希望,认为这是JDK的一部分,是标准的反映。
ArtOfWarfare 2015年

23

这个问题有多个答案,部分原因是模棱两可的问题-标题所讨论的是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();
        }
    }

}

整洁的事情之一是,您可以选择要检查的任意类加载器。但是,如果类加载器类的内部更改可能会中断,因此应将其用作一次性诊断工具。


@eis使用Thread.currentThread().getContextClassLoader()vs.有CPTest.class.getClassLoader()什么区别?您是否有理由希望ClassLoaderThread对象中获取?
Michael Plautz

@MichaelPlautz的区别在于,另一个是线程上下文类加载器,另一个是给定类使用的类加载器。java使用了许多不同的类加载器,我将不在此注释空间中详细说明。原因是假设线程上下文类加载器的父关系将包括整个JVM,而类加载器则可能不包括。
eis

1
您可以while通过这样做避免循环ClassLoader.class.getDeclaredField("classes")
metasim '16

在android中,我明白了java.lang.NoSuchFieldException: No field classes in class Ljava/lang/ClassLoader,为什么有任何想法?
内森·H

1
android中的@NathanH没有此类字段。对比Android的类加载器Java SE的类加载器
EIS

9

我也建议您编写一个-javagent代理,但是使用getAllLoadedClasses方法而不是转换任何类。

要与客户端代码(普通Java代码)同步,请创建一个套接字,并通过该套接字与代理进行通信。然后,您可以在需要时触发“列出所有类”方法。


7

上述方法的替代方法是创建一个外部代理,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加载给定类的信息。


3

如果您已经知道软件包的顶级路径,一种方法是使用OpenPojo

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

然后,您可以遍历列表并执行所需的任何功能。


2

您可能能够获得通过类加载器加载的类的列表,但这不包括尚未加载但位于类路径中的类。

要在类路径上获取所有类,您必须执行类似第二个解决方案的操作。如果您确实希望当前正在“加载”的类(换句话说,您已经引用,访问或实例化的类),则应优化问题以表明这一点。


2

在JRockit JVM下运行代码,然后使用 JRCMD <PID> print_class_summary

这将输出所有已加载的类,每行一个。


2

该程序将打印所有类及其物理路径。如果您需要分析从任何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();
        }
    }
}


-5

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


3
您可以提供一个代码段吗?这样其他人可以从您的解决方案中受益。谢谢
jtzero 2011年

5
@jtzero:我不知道使用什么OP,但是对我来说System.getProperty(“ java.class.path”)可以很好地工作。
LR

3
请阅读我如何写一个好的答案?在尝试回答更多问题之前。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.