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

我想列出属于某个包裹的所有课程以及他们所有的孩子。 这些类可能已经或可能未被加载到JVM中。

这不是一个程序化的解决scheme,但你可以运行

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] 

在这里看到更多的细节。

使用reflection库,这很容易:

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

这将扫描包含my.pkg包的url / s中的所有类。

  • false参数意味着 – 不排除默认排除的Object类。
  • 在一些情况下(不同的容器),你可能会传递classLoader和一个参数。

所以,获得所有类是有效地得到对象的所有子types,传递:

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

(普通的方式reflections.getSubTypesOf(Object.class)会导致加载所有类到PermGen,并可能会抛出OutOfMemoryError。你不想这样做…)

如果你想获得对象(或任何其他types)的所有直接子types,而不是一次获得其传递子types,使用这个:

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

这个问题有多个答案,部分原因是由于含糊不清的问题 – 标题正在讨论由JVM加载的类,而问题的内容是“可能会或可能不会被JVM加载”。

假设OP需要由JVM通过给定的类加载器加载的类,并且只有那些类 – 我也需要 – 这里有一个解决scheme( 详细说明在这里 ):

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

其中一个很好的事情是你可以select一个你想检查的任意类加载器。 然而,如果类加载器类的内部变化,它可能会被破坏,所以它被用作一次性的诊断工具。

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

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

上面描述的另一种方法是使用java.lang.instrument创build一个外部代理来找出哪些类被加​​载并用-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加载给定的类的信息。

一种方法,如果你已经知道包顶级path是使用OpenPojo

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

然后,您可以查看列表并执行您所需的任何function。

您可能能够获得通过类加载器加载的类的列表,但不包括尚未加载但在类path中的类。

要获得类path中的所有类,必须执行类似于第二种解决scheme的操作。 如果你真的需要当前“加载”的类(换句话说,你已经引用,访问或实例化的类),那么你应该优化你的问题来表明这一点。

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

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

这个程序将打印所有的类与它的物理path。 如果您需要分析从任何Web /应用程序服务器加载的类,可以将其简单地复制到任何jsp。

import java.lang.reflect.Field; import java.util.Vector;

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

}

那么,我所做的只是将所有文件列在类path中。 这可能不是一个光荣的解决scheme,但它可靠地工作,给我我想要的一切,甚至更多。