SlideShare a Scribd company logo
クラスローダーについて

   わかる! JVM
 あらかわ (@ashigeru)
本日の内容
 クラスロードの仕組み
 クラスローダーの名前空間
 リローディングの技術
 その他の話題
  アンロードの条件
  パッケージプライベート
  シリアライゼーション




          2010/08/06 #jvmjvm   2
2.17.2 Loading
5 Loading, Linking, and Initializing
5.3.2 Loading Using a User-defined Class Loader

クラスロードの仕組み


                      2010/08/06 #jvmjvm          3
クラスローダーの作り方
  1.   java.lang.ClassLoaderのサブクラスを定義
  2.   findClass()をオーバーライド
  3.   クラスファイルをdefineClass()に食わせる
  4.   結果のjava.lang.Classを返す

public class MyClassLoader extends ClassLoader {
  @Override
  protected Class<?> findClass(String name) throws … {
      byte[] content = …;
      Class<?> aClass = defineClass(name, content, 0, content.length);
      return aClass;
  }
  …

                              2010/08/06 #jvmjvm                    4
ロード処理の委譲
 ロード中に他のクラスローダーを利用できる
  loadClass()は最初に委譲先からクラスを検索
  委譲先になければfindClass()を起動




            2010/08/06 #jvmjvm   5
“推奨”の委譲スタイル
 すでにロード済みのクラスがあればそれを返す
 委譲先(親クラスローダー)でロード
 委譲先でロードできなければ自身でロード




         2010/08/06 #jvmjvm   6
“推奨”委譲スタイルの問題
 委譲先(親)とライブラリが衝突する
  親のライブラリの内容が優先される
  パッケージ名変更で乗り切るバッドノウハウ




          2010/08/06 #jvmjvm   7
現実の委譲スタイル
 某アプリケーションサーバー (PARENT_LAST)
   委譲先(親)を最後に検索する
    自身のクラスローダーをまず最初に検索
    親のライブラリに影響を受けにくい
 OSGi
   パッケージごとにクラスの公開/非公開を選べる
    委譲しても非公開クラスはロードできない
    「親の親」はデフォルトで非公開扱い
   複数の委譲先を持てる
    必要なライブラリだけを選べる

            2010/08/06 #jvmjvm   8
ここまでのまとめ
 java.lang.ClassLoaderを継承してカスタムクラ
  スローダーを作成
  findClass()をオーバーライドする
  defineClass()にクラスファイルバイナリを渡す
 他のクラスローダーにロードを委譲できる
  通常は委譲先から順にクラスを探索




              2010/08/06 #jvmjvm   9
5.3 Creation and Loading (defining loaders)
5.4.3.1 Class and Interface Resolution

クラスローダーの空間


                      2010/08/06 #jvmjvm      10
同じ名前のクラス
 同じ名前のクラスは単一JVM上に存在できる?
  WARなどが典型的




           2010/08/06 #jvmjvm   11
定義ローダー
 同じ名前でも定義ローダーが違えば別クラス
  defineClass()を実行したローダーのこと
  Class.getClassLoader()はこれを返す




              2010/08/06 #jvmjvm   12
参照先のロード
 自身の定義ローダーで参照先をロード
  下記のような依存関係は解決できない




          2010/08/06 #jvmjvm   13
PARENT_LASTの挙動
 子が定義ローダーになりライブラリが共存可能
  子になければ親のライブラリを利用




          2010/08/06 #jvmjvm   14
ドッペルゲンガー (1)
 PARENT_LASTでcommons-loggingを利用
  commons-loggingは親子ともに所持
  子のLogFactoryが優先される




              2010/08/06 #jvmjvm   15
ドッペルゲンガー (2)
 commons-loggingの実装を探しにいく
  子には特別の実装が無いため次に親を探す




             2010/08/06 #jvmjvm   16
ドッペルゲンガー (3)
 LogFactoryの実装にLog4jFactoryを利用
   Log4jFactoryの定義ローダーはCL1




               2010/08/06 #jvmjvm   17
ドッペルゲンガー (4)
 Log4jFactoryは親のLogFactoryを継承
   定義ローダーの関係で親のものを利用する
   LogFactoryが二つ出現




               2010/08/06 #jvmjvm   18
ドッペルゲンガー (5)
 Log4jFactoryをLogFactory(CL2)に変換失敗
   2つのLogFactoryは完全に別物
   推奨の移譲スタイルはそれなりに意味がある




               2010/08/06 #jvmjvm     19
ここまでのまとめ
 同じクラスでも定義ローダーが違えば別物
  ドッペルゲンガーが出現することも
 参照先のクラスは定義ローダーを使う
  PARENT_LASTなどと組み合わせると混乱する




           2010/08/06 #jvmjvm   20
クラスローダーを利用した擬似的なクラスリローディング

リローディングの技術


            2010/08/06 #jvmjvm   21
リローディングの技術
 JVMを立てたままクラスをリロードして即座に
  変更を反映
  JUnit 3系 – GUIについてたアレ
  Seasar 2 – Hot Deploy
  他にもいろいろあると思う




              2010/08/06 #jvmjvm   22
リローディングの仕組み (1)
 リローディング用のクラスローダーを子に作成
  親には利用したいクラスローダーを指定




          2010/08/06 #jvmjvm   23
リローディングの仕組み (2)
 親からクラスファイルのバイナリを拝借
  parent.getResourceAsStream() など




                2010/08/06 #jvmjvm   24
リローディングの仕組み (3)
 リローディング用のクラスローダーで定義
  親を探しに行かない
  普通はパッケージを限定する




          2010/08/06 #jvmjvm   25
リローディングの仕組み (4)
 リクエストごとにクラスローダーを作り直す
  リクエストごとにクラスをリロード
  クラスの更新を反映させられる




          2010/08/06 #jvmjvm   26
リローディングとセッション
 セッション情報でドッペルゲンガー




          2010/08/06 #jvmjvm   27
ここまでのまとめ
 定義ローダーを毎回捨ててリローディング
  強制的に自分が定義ローダーになるようにする
 モデルの引継ぎが意外と大変
  自作するとたいていハマる




          2010/08/06 #jvmjvm   28
2.17.8 Unloading of Classes and Interfaces
5.3 Creation and Loading (runtime packages)
(Object Serialization Specification)

その他の話題


                     2010/08/06 #jvmjvm       29
ネタ
 アンロードの条件
 パッケージプライベート
 シリアライゼーション




          2010/08/06 #jvmjvm   30
アンロードの条件 (1)
 このプログラムはどうなる?
  C0, C1, C2, …は全てロード可能とする



ClassLoader cl = new InfiniteClassLoader();
for (long i = 0; i < Long.MAX_VALUE; i++) {
   cl.loadClass("C" + i);
}




                    2010/08/06 #jvmjvm        31
アンロードの条件 (2)
 クラスローダーがGCで回収可能になるまで、ク
  ラスは一つもアンロードされない
  先ほどの例は80,000~くらいでメモリ枯渇した

 インスタンスやjava.lang.Classを保持している
  とクラスローダーがGC回収可能にならない
  一つでもリークしたら全てのクラスがリーク
  共有空間でキャッシュしてたりすると大変
  ライフサイクルの管理が重要になる


             2010/08/06 #jvmjvm   32
アンロードの条件 (3)
 クラスをアンロードさせたい場合には定期的に
  クラスローダーを捨てる
  キャッシュするならライフサイクルに注意
  特に共有空間に漏れないように
  動的クラス生成を行うフレームワークは要注意
  アスペクトウィービング
  スクリプト言語のJITコンパイラ
 ただし、アンロードは「最適化」という扱い
  メモリ効率の向上
  アンロード自体に対応していない可能性も

           2010/08/06 #jvmjvm   33
アンロードの条件 (4)
 2,000,000クラスまで確認 (飽きた)
   クラスローダーを毎回捨てている




ClassLoader cl = new InfiniteClassLoader();
for (long i = 0; i < Long.MAX_VALUE; i++) {
   ClassLoader cl = new InfiniteClassLoader();
   cl.loadClass("C" + i);
}


                    2010/08/06 #jvmjvm           34
パッケージプライベート (1)
 パッケージプライベートのアクセス制御は?
     package a;
     public class Hoge {
       /*package*/ int foo() {
           return 100;
       }
     }

     package a;
     public class HogeTest {
       @Test
       public void about_package() {
           Hoge hoge = new Hoge();
           assertThat(hoge.foo(), is(100));
       }
     }

                  2010/08/06 #jvmjvm          35
パッケージプライベート (2)
 同じパッケージで別のクラスローダー
  OSGi環境でのテストでよくやる




           2010/08/06 #jvmjvm   36
実行時パッケージ
 実行時パッケージごとにプライベート
  クラスの定義ローダーごとに別パッケージ扱い
  先ほどの例は “IllegalAccessError”




            2010/08/06 #jvmjvm   37
シリアライゼーション
 ObjectInputStreamが使うクラスローダーは?




             2010/08/06 #jvmjvm   38
ObjectInputStream (1)
 一番近いユーザー定義クラスローダーを利用
  コールスタック上のクラスの定義ローダー
  この例ではBarのCL1を利用してデシリアライズ




           2010/08/06 #jvmjvm   39
ObjectInputStream (2)
 親ローダーにシリアライゼーションフレームワ
  ークを置いても使いにくい
  フレームワーククラスの定義ローダーを常に利用
  ObjectInputStreamにはクラスローダーを指定
   できない
 以下のメソッドを上書きすれば対応は可能
  resolveClass
  resolveProxy




                  2010/08/06 #jvmjvm   40
ObjectInputStream (3)
 以下の組み合わせは怪しい
  カスタムクラスローダー
  オブジェクト永続化 (XML, JSON等も含む)

 OSGi + Object DBは個人的に鬼門
  どのクラスローダーでロードすれば?
  クラスローダーをまたいでネストしたモデルは?
  あまりいい思い出がない



              2010/08/06 #jvmjvm   41
参考文献
 Java仮想マシン仕様 第2版
    ISBN: 4-89471-356-X
 Java言語仕様 第3版
    ISBN: 4-89471-715-8
 JSR 202: Java Class File Specification Update
    http://jcp.org/en/jsr/summary?id=202
 “オブジェクト直列化形式”
    http://java.sun.com/javase/ja/6/docs/ja/techno
     tes/guides/serialization/



                      2010/08/06 #jvmjvm          42

More Related Content

クラスローダーについて