web-dev-qa-db-ja.com

特定のクラスローダーにロードされたすべてのクラスをリストするにはどうすればよいですか

デバッグ上の理由と好奇心から、特定のクラスローダーにロードされたすべてのクラスを一覧表示したいと思います。

クラスローダーのほとんどのメソッドが保護されているのを見て、私が望むことを達成する最良の方法は何ですか?

ありがとう!

39
Yaneeve

Instrumentation.getInitiatedClasses(ClassLoader)はあなたが望むことをするかもしれません。

ドキュメントによると:

ローダーが開始ローダーであるすべてのクラスの配列を返します。

しかし、「ローダーを開始する」の意味がわかりません。正しい結果が得られない場合は、getAllLoadedClasses()メソッドを使用し、ClassLoaderで手動でフィルタリングしてみてください。


Instrumentationのインスタンスを取得する方法

Instrumentationインターフェイスのインスタンスを取得できるのは、エージェントJAR(アプリケーションJARとは別)のみです。アプリケーションで使用できるようにする簡単な方法は、システムプロパティにpremainインスタンスへの参照を保存するだけのInstrumentationメソッドを持つ1つのクラスを含むエージェントJARを作成することです。

エージェントクラスの例:

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に2回ロードされる場合がありますが、システムPropertiesはプロセスごとのシングルトンであるため、問題はありません。

サンプルアプリケーションクラス

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

これを試して。それはハッカー的な解決策ですが、それはします。

クラスローダ(1.0以降のSunの実装下)のclassesフィールドは、ローダによって定義されたクラスへのハード参照を保持するため、GCされません。リフレクションを介して恩恵を受けることができます。

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

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes =  (Vector<Class>) f.get(classLoader);
56
bestsss