javaのクラスローダに関して下記のようなコードが存在するときの内部動作がよくわからなかったので調べ所を残すことにしてみました。
Class clazz = App.class; ClassLoader cl = clazz.getClassLoader();
クラスとクラスローダについて
さてこの getClassLoader というメソッドは何なのか、ということですが文字通りクラスローダを取得することができます。
このメソッドは Class クラスのメソッドですが、例えばアプリケーションを実装していて同プロジェクトに B というクラスと C というクラスが作成したときに下記のようなコードが存在したときに、この違いはあるんでしょうか?
ClassLoader clB = B.class.getClassLoader(); ClassLoader clC = C.class.getClassLoader();
先に答えを言ってしまうと、この2つで返却されるクラスローダは同じものになります。
理由ですがこの getClassLoader() を呼び出すとクラスは自分の参照するクラスローダを返却します。
すべてのクラスは必ず一つのクラスローダへの参照を持っており、それは自クラスをロードしたときに使用されたクラスローダを参照するようになっています。
そのため B, C は同プロジェクトのアプリケーションとして実装されているので、この2つは同じクラスローダによってロードされるため上記のような場合は大抵は同じクラスローダが返却されます。
クラスローダによるクラスの解決について
またクラスローダは自分の解決するクラスの領域というものが決まっており、階層構造を持ちます。
これはDNSで各ネームサーバが自分のゾーンだけを管理して、解決できない場合は親ネームサーバへと処理を移譲する仕組みと同じようなものです。
javaのデフォルトパッケージを解決するクラスローダのことを「ブートストラップクラスローダ」と呼び、これは一番親のクラスローダとして位置します。
さらにその下にアプリケーションのクラスを解決するための「システムクラスローダ」というクラスローダが存在します。
そんなに複雑でないシステムの場合はこの「システムクラスローダ」がアプリケーションクラスのクラスローダとして機能すると考えていて問題ないと思います。
クラスローダはクラスロードの依頼を受けた際にはまず親のクラスローダに対して解決を移譲します。
もしクラスが解決できないようであれば自分自身の管理する空間を探索します。
例えば下記のようなサンプルコードを用意して実行してみましょう。
ちなみにこのコードはgroovyにて記述してあります。
class Main { static void main(String[] args) { ClassLoader cl = Main.class.getClassLoader() println cl } }
この実行結果は下記のように出力されました
sun.misc.Launcher$AppClassLoader@18b4aac2
「システムクラスローダ」というのは一般的な機能に対する名称のようで、クラス実態としては sun.misc.Launcher.AppClassLoader というクラスが実態のようです。
通常はこのシステムクラスローダがすべてのアプリケーションクラスのクラスローダとして機能します。
異なるクラスローダが用いられるアプリケーションはあるのか
主にJ2EEサーバなどにおいてはwarをおいただけで異なるアプリケーションをホストできる仕組みを提供しているため、各種ウェブアプリケーション単位でシステムクラスローダが異なるような形になると思われます。
設置した複数のwarファイルの中で同じ名称のクラスが存在する際に、うまく解決できないようではアプリケーションとして致命的ですので、このような場合にうまく機能してくる仕組みだと思われます。
通常はひとつのプロジェクトが固有の一つのクラスローダを持つ、、的な理解で問題ないと思います。
参考ページ
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ClassLoader.html
http://www.nminoru.jp/~nminoru/java/class_unloading.html
コメントを残す