如何列出在特定类加载器中加载的所有类

Posted

技术标签:

【中文标题】如何列出在特定类加载器中加载的所有类【英文标题】:How can I list all classes loaded in a specific class loader 【发布时间】:2011-02-10 12:10:49 【问题描述】:

出于调试原因和好奇,我希望列出加载到特定类加载器的所有类。

鉴于类加载器的大多数方法都受到保护,实现我想要的最佳方法是什么?

谢谢!

【问题讨论】:

另见:forums.sun.com/thread.jspa?threadID=341935&start=15 【参考方案1】:

试试这个。这是一个骇客的解决方案,但它会做。

任何类加载器中的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 @NathanH,从技术上讲,android 甚至不是 Java(直到它获得 OpenJDK),但正如我所提到的,它是一个骇客的解决方案,依赖于类加载器的实施方式已经超过 17 年。也不能保证提议的黑客在将来会起作用。同样在 Android 上,我真的看不到运行类似中间件的代码(使用动态类加载),所以应该不需要获取加载的类。 Android 可以做动态类加载,我一直用JavaX 做。事实上,我以这种方式加载所有主要程序(该应用程序只是一个存根)。 有一段时间,ClassLoader 类的字段无法通过反射访问。即使使用setAccessible 方法。见***.com/a/17805624/9710708【参考方案2】:

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

【讨论】:

但是如何获取 Instrumentation 实例呢? @Arne Burmeister,查看包说明:java.sun.com/javase/6/docs/api/java/lang/instrument/… 所以如果我理解正确,我必须编写一个代理,其中包含一个签名为“public static void premain(String agentArgs, Instrumentation inst);”的方法然后通过 inst ref 调用 Instrumentation 方法? @Yaneeve,是的。在我的答案中添加了一个示例。 Initiating classloader 是第一个被请求加载类的类加载器,无论哪个类加载器定义了类(即使引导程序确实如此)。这个概念对Class.forName [即通过本机代码],但不适用于直接调用 ClassLoader.loadClassClassLoader.findLoadedClass 会准确检查,如果具有给定名称的类已记录在预订中,并由类加载器初始化。

以上是关于如何列出在特定类加载器中加载的所有类的主要内容,如果未能解决你的问题,请参考以下文章

libGDX / roboVM:如何修复“已在另一个类加载器中加载的本机库libhfscompressor.dylib”?

类加载器

在 CI 模板加载器类中加载一个或多个视图

java类加载器?

如何使用 Next.js 访问当前在导航中加载的 url 或页面

JVM类加载器篇(上)