Как я могу перечислить все классы, загруженные в определенный загрузчик классов - PullRequest
39 голосов
/ 21 апреля 2010

По причинам отладки и любопытства я хочу перечислить все классы, загруженные в определенный загрузчик классов.

Поскольку большинство методов загрузчика классов защищены, как лучше всего выполнить то, что я хочу?

Спасибо!

Ответы [ 2 ]

55 голосов
/ 21 апреля 2012

Попробуй это. Это хакерское решение, но оно подойдет.

Поле classes в любом загрузчике классов (в соответствии с имплементацией Sun начиная с 1.0) содержит жесткую ссылку на классы, определенные загрузчиком, поэтому они не будут GC'd. Вы можете извлечь выгоду из размышлений.

Field f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes =  (Vector<Class>) f.get(classLoader);
32 голосов
/ 21 апреля 2010

<a href="http://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html#getInitiatedClasses(java.lang.ClassLoader)" rel="noreferrer">Instrumentation.getInitiatedClasses(ClassLoader)</a> можете делать то, что вы хотите.

Согласно документам:

Возвращает массив всех классов, для которых загрузчик является загрузчиком-инициатором.

Я не уверен, что означает "загрузчик инициации". Если это не дает правильного результата, попробуйте использовать метод 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) при запуске приложения. Он может быть загружен дважды в разные ClassLoader с, но это не проблема, поскольку система Properties является синглтоном для каждого процесса.

Пример класса приложения

public class Main {
    public static void main(String[] args) {
        Instrumentation inst = InstrumentHook.getInstrumentation();
        for (Class<?> clazz: inst.getAllLoadedClasses()) {
            System.err.println(clazz.getName());
        }
    }
}
...