クラスローダメモその1
クラスローダとはクラスをメモリ上にロードするもの。
J2SEでは以下の3つがある。自作することも可能。
- ブートストラップクラスローダ:rt.jarのクラスローダ
- 拡張クラスローダ:lib/extのクラスローダ
- システムクラスローダ:classpathのクラスローダ
クラスローダには親子関係があり、子は親を知っているが、親は子を知らない。兄弟も知らない。
子クラスローダに、クラスのロードの依頼があると、親に委譲し、最初に発見されたクラスがロードされる(parent first)。
具体的にjava.lang.ClassLoaderのloadClassメソッドを見ると、
findLoadedClassですでにロードされていないか調べて、もしロードされていないなら、親に委譲(ブートストラップクラスローダまで)。
見つからなかったら、findClassで探す。となっている。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
実際に、以下のサンプルを試すと、
public class Main { public static void main(String[] args) { // Bootstrap class loader System.out.println("●ブートストラップ・クラスローダ"); ClassLoader bootstrap = String.class.getClassLoader(); System.out.println(bootstrap); if (bootstrap != null) { ClassLoader parent = bootstrap.getParent(); System.out.println(bootstrap + "の親は" + parent); } else { System.out.println("親なし"); } // 拡張クラスローダ System.out.println("●拡張クラスローダ"); ClassLoader extensions = sun.net.spi.nameservice.dns.DNSNameService.class .getClassLoader(); System.out.println(extensions); if (extensions != null) { ClassLoader parent = extensions.getParent(); System.out.println(extensions + "の親は" + parent); } else { System.out.println("親なし"); } // システム・クラスローダ System.out.println("●システム・クラスローダ"); ClassLoader systems = DemoClass.class.getClassLoader(); System.out.println(systems); if (systems != null) { ClassLoader parent = systems.getParent(); System.out.println(systems + "の親は" + parent); } else { System.out.println("親なし"); } // コンテキスト・クラスローダ System.out.println("●コンテキスト・クラスローダ"); ClassLoader threads = Thread.currentThread().getContextClassLoader(); System.out.println(threads); if (threads != null) { ClassLoader parent = threads.getParent(); System.out.println(threads + "の親は" + parent); } else { System.out.println("親なし"); } } } //テスト用クラス class DemoClass { String getClassName() { return "DemoClass"; } }
以下のように出力される。ここでコンテキスト・クラスローダとは、そのスレッド内でクラスをロードする場合に使用されるクラスローダのことです。
●ブートストラップ・クラスローダ
null
親なし
●拡張クラスローダ
sun.misc.Launcher$ExtClassLoader@35ce36
sun.misc.Launcher$ExtClassLoader@35ce36の親はnull
●システム・クラスローダ
sun.misc.Launcher$AppClassLoader@11b86e7
sun.misc.Launcher$AppClassLoader@11b86e7の親はsun.misc.Launcher$ExtClassLoader@35ce36
●コンテキスト・クラスローダ
sun.misc.Launcher$AppClassLoader@11b86e7
sun.misc.Launcher$AppClassLoader@11b86e7の親はsun.misc.Launcher$ExtClassLoader@35ce36
一方、Tomcatでは親に委譲する前に自分で探している(parent last。delegateフィールドはデフォルトfalseっぽい)。
WebappClassLoader
boolean delegateLoad = delegate || filter(name); // (1) Delegate to our parent if requested if (delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader1 " + parent); ClassLoader loader = parent; if (loader == null) loader = system; try { clazz = loader.loadClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) { ; } } // (2) Search local repositories if (log.isDebugEnabled()) log.debug(" Searching local repositories"); try { clazz = findClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from local repository"); if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) { ; } // (3) Delegate to parent unconditionally if (!delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader at end: " + parent); ClassLoader loader = parent; if (loader == null) loader = system; try { clazz = loader.loadClass(name); if (clazz != null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return (clazz); } } catch (ClassNotFoundException e) { ; } }
参考: