Calendarの25日目の記事です。実践入門でした!本記事の趣旨は、developerWorksのクラスローダーとJ2EEパッケージング戦略を理解するに書いてあることをコードで確かめようというものです。昨今はOSGiの登場などによりあまり目立たなくなったのかもしれませんが、未だにクラスローダはJ2EEアプリケーションの実装・運用において重要な役割を担っています。本記事がクラスローダの理解に役立てば幸いです。はじめに:クラスローダとは?クラスローダとは、クラス定義をclassファイルから読み込んでくれるものです。通常のJavaアプリケーションではJVMが用意する複数の基本クラスローダが存在し、このクラスローダがJREのクラスとCLASSPATHにあるクラスを読み込んでくれています。各クラスローダは1つの親クラスローダを持っていて、自分でロードする前に親にロードを委譲したり、自分でクラスを見つけられなかったときに親に問い合せたりします。エンタープライズウェブアプリケーションの場合は基本クラスローダだけではなく、アプリケーションサーバがEARごと・WARごとにクラスローダを作成しています*1。このため、アプリケーションごとに異なるライブラリを使用することが可能です。WARと呼ばれる技法によりEARのファイルサイズを減らすこともできます。8.0の公式ドキュメントクラスロードポリシークラスローダはクラスを読み込んでくれるものだと書きましたが、その探し方には2パターンあります。1つ目は「親が最初」、つまりまずは親クラスローダに読み込みを依頼するというものです。親クラスローダが対象クラスを見つけた場合はそれを返し、見つけられなかった場合は自分が参照できるJARファイルやclassファイルからの発見を試みます。クラスローダのデフォルトの挙動はこの「親が最初」になっています。http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html2つ目は「親が最後」です。自分でクラスを探し、見つからなかったときにだけ親に委譲します。GitHubに「親が最後」クラスローダの簡易実装を置いてあります。クラスのアンロードクラスローダはロードしたクラスをメモリ上のパーマネント領域に記録します。ロードされたクラスはクラスローダおよびクラスの両方が使われなくなったときにアンロードされ、メモリが解放されます。アプリケーションサーバを起動したまま(JVMを動作させたまま)ウェブアプリケーションを再起動すると、かつて使っていたクラスローダとクラスはGCとアンロードによってメモリから消去されます。新しくクラスローダが作られ、新規にクラスがロードされていきます。Unloading)が参考になります。以上を踏まえて、トラブルを見ていきましょう。warやearにパッケージしたライブラリが使われない問題クラスローダはデフォルトで「親が最初」ポリシーを利用するため、Java基本クラスローダなどの親クラスローダが同じ名前のクラスを持っていた場合、子クラスローダは自分が参照している場所からクラスを読み込むことがありません。このため以下のテストケースがpassします。親も子も同じクラスを参照するため、各クラスローダから取ってきたシングルトンインスタンスが等しいことがわかります。}このため以下のようなトラブルが発生した場合は、親クラスローダがパッケージされているものと同じライブラリを持っていないか確認する必要があります。JREの差に原因があるかもしれないアプリケーションサーバのクラスローダがライブラリを読み込んでいるかもしれないJREのlib/extにライブラリが突っ込んであるかもしれないシングルトンインスタンスが2つ以上存在している(後述)シングルトンがシングルトンじゃない問題developerWorksにも細かい解説が載っていますが、シングルトンパターンで使用するようなstaticフィールドは「JVM内に1つしかない」のではなく「ロードされたクラスごとに1つずつある」ため、以下のような条件下で期待と異なる動作をします。以下のテストケースは上記2パターンでシングルトンインスタンスが複数存在してしまうケースを説明するものです。}こうした問題もあるので、今であればJ2EE環境では自前でシングルトンを実装するのではなく、環境が提供する手法(@Singletonアノテーションとか)に頼ったほうが良いでしょう。テスト可能性も向上するはずです。上述したとおり、クラスがアンロードされるためにはクラスとクラスローダの双方が利用されない必要があります。利用されないというのは、それらのインスタンスやstaticフィールド、Classインスタンスなどへの参照がなくなることです。例として以下のテストを見てみます。クラスとクラスローダに対する参照は破棄されているもののインスタンスに対する参照(singletonInstance変数)が残っているため、クラスがアンロードされません。}本番環境では、Java基本クラスローダやアプリケーションサーバのクラスローダがロードしたクラスからの参照がリークの原因となります。事例をまとめた資料として、nekopさんのクラスローダーリークパターンがすばらしいです。Logging)あるいはJUL(java.util.logging)を使用されている場合は必見です。おわりに以上、駆け足でしたがクラスローダとそれに関連するトラブルを紹介しました。クラスローダはJVMの基本のひとつであり、J2EEではEJBなどでも利用されています。理解を深めて気軽に触れるようになっておきましょう。サンプルコード本記事で使用したコードはすべてGitHubから入手いただけます。クラスローダ実装例ないしJUnitの使い方としても使えそうです。eller86/j2ee-class-loader-example関連記事クラスローダ自作はバイトコードを生成・改変する際にも行います。ASMによるクラスの動的定義で使用しています。Entry2013-04-26altWebSiteなるものを妄想してみるHTML/CSS/JavaScriptを複雑に組み合わせてアプリケーションを作...2012-07-23enchant.jsはモジュール化するといいと思うenchant.jsをもっと簡単に使えるようにしようという話が大変興...2011-10-03mockitoコードリーディング備忘録オフラインになっていた間、mockitoのコードを読んでいました。