【发布时间】:2011-02-10 12:10:49
【问题描述】:
出于调试原因和好奇,我希望列出加载到特定类加载器的所有类。
鉴于类加载器的大多数方法都受到保护,实现我想要的最佳方法是什么?
谢谢!
【问题讨论】:
标签: java hierarchy classloader
出于调试原因和好奇,我希望列出加载到特定类加载器的所有类。
鉴于类加载器的大多数方法都受到保护,实现我想要的最佳方法是什么?
谢谢!
【问题讨论】:
标签: java hierarchy classloader
试试这个。这是一个骇客的解决方案,但它会做。
任何类加载器中的classes 字段(自 1.0 以来在 Sun 的 impl 下)持有对加载器定义的类的硬引用,因此它们不会被 GC'd。您可以通过反射受益。
Field f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes = (Vector<Class>) f.get(classLoader);
【讨论】:
No field classes in class Ljava/lang/ClassLoader
setAccessible 方法。见stackoverflow.com/a/17805624/9710708
@987654321@ 可以为所欲为。
根据文档:
返回一个由加载器作为初始加载器的所有类组成的数组。
我不确定“启动加载程序”是什么意思。如果这不能给出正确的结果,请尝试使用 getAllLoadedClasses() 方法并通过 ClassLoader 手动过滤。
如何获取Instrumentation的实例
只有代理 JAR(独立于应用程序 JAR)才能获得Instrumentation 接口的实例。使其可用于应用程序的一种简单方法是创建一个代理 JAR,其中包含一个具有 premain 方法的类,该方法只在系统属性中保存对 Instrumentation 实例的引用。
示例代理类:
public class InstrumentHook {
public static void premain(String agentArgs, Instrumentation inst) {
if (agentArgs != null) {
System.getProperties().put(AGENT_ARGS_KEY, agentArgs);
}
System.getProperties().put(INSTRUMENTATION_KEY, inst);
}
public static Instrumentation getInstrumentation() {
return (Instrumentation) System.getProperties().get(INSTRUMENTATION_KEY);
}
// Needn't be a UUID - can be a String or any other object that
// implements equals().
private static final Object AGENT_ARGS_KEY =
UUID.fromString("887b43f3-c742-4b87-978d-70d2db74e40e");
private static final Object INSTRUMENTATION_KEY =
UUID.fromString("214ac54a-60a5-417e-b3b8-772e80a16667");
}
清单示例:
Manifest-Version: 1.0
Premain-Class: InstrumentHook
然后,应用程序必须引用生成的 JAR。并且在启动应用程序时在命令行中指定(使用 -javaagent 选项)。它可能会在不同的ClassLoaders 中加载两次,但这不是问题,因为系统Properties 是每个进程的单例。
示例应用程序类
public class Main {
public static void main(String[] args) {
Instrumentation inst = InstrumentHook.getInstrumentation();
for (Class<?> clazz: inst.getAllLoadedClasses()) {
System.err.println(clazz.getName());
}
}
}
【讨论】:
Class.forName [即通过本机代码],但不适用于直接调用 ClassLoader.loadClass。 ClassLoader.findLoadedClass 会准确检查,如果具有给定名称的类已记录在预订中,并由类加载器初始化。