JavaでのClassLoaderの動き(まとめ)
※ソースと出力結果が間違っていたので一部修正(2010/5/11)
プロジェクトでクラスがロードされるのが遅い*1とかで調べることがあったので、実際どんな条件でロードしているのかをまとめてみた。そもそも、クラスを小さく(適切な大きさに)していればClassLoaderの動きなんて調べなくてもいいはずなんだけど、そこは大人の事情ってやつで。
ちなみに、ホントに細かい動作とかは使ってるVMとかに影響されるので100%これが正しいってわけではないと思います。
まぁ、参考までに結構動きが変わるってことがわかればいいかなと
調査方法
基本的には、事あるごとにClassLoaderからロード済みのクラスをSystem.outして、どんな感じでロードしているかを調べてみた。
流れとしては、
で、それぞれgetBuzz(int)から先の動きは以下のとおり
int | 返り値 | クラス名 | インスタンス生成方法 |
---|---|---|---|
0 | (Buzz0) | Buzz0 | コンストラクタを直接 |
1 | Buzz | Buzz1 | Buzz1.getInstance()内でBuzz1のコンストラクタ生成 |
2 | Buzz2 | Buzz2 | Buzz2.getInstance()内でBuzz2のコンストラクタ生成 |
3 | Buzz | Buzz3 | Hoge#createBuzz3()内でBuzz3のコンストラクタ生成 |
4 | Buzz4 | Buzz4 | Hoge#createBuzz4()内でBuzz4のコンストラクタ生成 |
5 | Buzz | Buzz5 | Factory.createBuzz5()内でBuzz5のコンストラクタ生成 |
6 | Buzz6 | Buzz6 | Factory.createBuzz6()内でBuzz6のコンストラクタ生成 |
7 | Buzz | Buzz7 | リフレクションによる生成 |
8 | Buzz8 | Buzz8 | リフレクションによる生成 |
結果
(0,2,4,6,8)はgetBuzz(int)の返り値Buzzと、生成時の返り値がBuzz以外のものは、Hogeがロードされた段階で(今回はHoge内にmainがあるのでまとめて呼ばれてしまっている)getBuzz(int)内で返される可能性のあるものすべてがクラスロードされている。
(3)も基本的にはHogeのクラスロード時に読み込まれるけど、メソッドが別なのでワンテンポ遅れてるっぽい
(1,5,7)は実際にメソッドがキックされたときにクラスがロードされている
(7)は生成はしているはずなのにClassLoader内に含まれていない(謎)
まとめ
リフレクションは別格だとしても、返り値は具象クラスを指定するより、抽象クラスやインターフェースを指定したほうが、最小限のクラスロードですみそう。
そのために、ファクトリーメソッドを用意してメソッド内のクラスの粒度(?)をそろえるのが良さそう。*2
以下、参考資料
環境
JDK1.6
出力結果
### init #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 #### created #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 #### getBuzz0(limelabo.Hoge$Buzz0) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 #### getBuzz1(limelabo.Hoge$Buzz1) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 #### getBuzz2(limelabo.Hoge$Buzz2) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 #### getBuzz3(limelabo.Hoge$Buzz3) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 #### getBuzz4(limelabo.Hoge$Buzz4) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 #### getBuzz5(limelabo.Hoge$Buzz5) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 class limelabo.Hoge$Facotory class limelabo.Hoge$Buzz5 #### getBuzz6(limelabo.Hoge$Buzz6) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 class limelabo.Hoge$Facotory class limelabo.Hoge$Buzz5 #### getBuzz7(limelabo.Hoge$Buzz7) #### class limelabo.Hoge class limelabo.Hoge$Buzz class limelabo.Hoge$Buzz0 class limelabo.Hoge$Buzz2 class limelabo.Hoge$Buzz4 class limelabo.Hoge$Buzz6 class limelabo.Hoge$Buzz8 class limelabo.Hoge$Buzz3 class limelabo.Hoge$Buzz1 class limelabo.Hoge$Facotory class limelabo.Hoge$Buzz5 class limelabo.Hoge$Buzz7
ソースコード
package limelabo; import java.lang.reflect.Field; import java.util.Vector; public class Hoge { public static void main(String[] args) { ClassLoader cl = ClassLoader.getSystemClassLoader(); printClassLoader("#### init ####", cl); Hoge hoge = new Hoge(); printClassLoader("#### created ####", cl); for (int i = 0; i < 8; i++) { Buzz buzz = hoge.getBuzz(i); printClassLoader("#### getBuzz" + i + "(" + buzz.getClass().getName() + ") ####", cl); } } private Buzz getBuzz(int i) { switch (i) { case 0: return new Buzz0(); case 1: // 返り値はBuzz return Buzz1.getInstance(); case 2: // 返り値はBuzz2 return Buzz2.getInstance(); case 3: // 返り値はBuzz return createBuzz3(); case 4: // 返り値はBuzz4 return createBuzz4(); case 5: // 返り値はBuzz return Facotory.createBuzz5(); case 6: // 返り値はBuzz6 return Facotory.createBuzz6(); case 7: // 返り値はBuzz return createBuzz7ByReflection(); case 8: // 返り値はBuzz8 return createBuzz8ByReflection(); default: throw new IllegalArgumentException("arg is " + i); } } private Buzz createBuzz3() { return new Buzz3(); } private Buzz4 createBuzz4() { return new Buzz4(); } static abstract class Buzz { } static class Buzz0 extends Buzz { } static class Buzz1 extends Buzz { static Buzz getInstance() { return new Buzz1(); } } static class Buzz2 extends Buzz { static Buzz2 getInstance() { return new Buzz2(); } } static class Buzz3 extends Buzz { } static class Buzz4 extends Buzz { } static class Buzz5 extends Buzz { } static class Buzz6 extends Buzz { } static class Buzz7 extends Buzz { } static class Buzz8 extends Buzz { } static class Facotory { static Buzz createBuzz5() { return new Buzz5(); } static Buzz6 createBuzz6() { return new Buzz6(); } } private Buzz createBuzz7ByReflection() { try { return (Buzz) Class.forName("limelabo.Hoge$Buzz7").newInstance(); } catch (Exception e) { // ignore } return null; } private Buzz8 createBuzz8ByReflection() { try { return (Buzz8) Class.forName("limelabo.Hoge$Buzz8").newInstance(); } catch (Exception e) { // ignore } return null; } private static void printClassLoader(String message, ClassLoader cl) { System.out.println(message); Class<?> c = cl.getClass(); // ClassLoaderまでさかのぼる for (; !ClassLoader.class.getName().equals(c.getName()); c = c.getSuperclass()) { } try { Field classesField = c.getDeclaredField("classes"); classesField.setAccessible(true); Vector<Class> classes = (Vector<Class>) classesField.get(cl); for (Class loadedClass : classes) { System.out.println(loadedClass); } } catch (Exception e) { // ignore } } }