clojureのクラスリロード
前回もクラスローダのあたりを書いたがあんまりしっくりしないのでもうちょっと調べてみる. JavaのClassLoaderの仕組みが分かっていなかった. まずStack OverflowのHow does clojure class reloading work?から. 質問 私はclojureでクラスのリロードの仕組みについてコードやドキュメントを 調べてきました. 多くのwebサイト,例えばhttp://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html によると,クラスのロードは本質的に どんなデータ構造であれバイト列を得ることでそれを defineClassによりclassClassのインスタンスに変換し, resolveClassによりそのclassをresolve(link)することであるということでした. (defineClassは暗黙的にresolveClassを呼んでいるのでしょうか?) どんなclassloaderでもclassのリンクは一度だけに限定されています. もし既存のclassへリンクしようとした場合でも何も行われないようです. これでは新しいclassインスタンスをリンクできないという問題があり, classをリロードする際は毎度classloaderのインスタンスを作る必要があります. clojureに注目してみると,clojureには複数のクラス定義方法があります. 匿名クラス: reify proxy 名前付きクラス: deftype defrecord(内部ではdeftypeを使っている) gen-class これらに関するコードは最終的にclojure/src/jvm/clojure/lang/DynamicClassLoader.java へたどり着きます. DynamicClassLoader/defineClassではclassインスタンスを作り,キャッシュしておきます. クラスをロードし利用する場合はforNameを呼び,またそれはDynamicClassLoader/findClassを 呼びます. DynamicClassLoader/findClassは,キャッシュを検索しその後基底クラスを検索します. (通常のclassloaderでは逆に基底クラスを先に検索するようです.) 混乱の元になっているのは, forNameはclassをリンクする前に返す(returnする)とドキュメントにある これは既存のDynamicClassLoaderではclassのリロードが行えない 代わりに新しくDynamicClassLoaderのインスタンスを作らねばならないがそのコードが見つからない ということです. proxyやreifyは匿名クラスなのでクラス名が異なっていても問題ありません. しかし,名前付きクラスの場合はそうはいきません. DynamicClassLoaderの仕組みを教えてください. 最終的にはjavacでコンパイルされた.classファイルをロード,リロードさせたいのです. 回答 以下全てが同じ技術を使っているわけではありません. proxy proxyマクロは基底クラスやインタフェースから名付けられたクラスを作ります. それぞれのメソッドはインスタンス内でclojureのfnとして扱われるため, マクロの内部が同じか否かに依らず, 同じインタフェースが継承される場合は同じproxyのクラスが使われます. 実際にクラスのリロードは行われません. reify reifyの場合は,メソッド本体が直接classの中にコンパイルされるため, proxyのトリックは使えません. 代わりに,formがコンパイルされる度に新たなクラスができるため, もし本体を編集してリロードしたとしても完全に新たなクラス(新しい名前)が作られます. これも実際のクラスのリロードは行われません. gen-class gen-classではクラス名を指定するため,proxyやreifyとは異なります. gen-classマクロは枠組み(spec)のみを持ち,メソッド本体は持ちません. つまりproxyと似て,clojureのfnを参照するということです. しかしproxyと違いクラス名が枠組み(spec)と結びつくため,本体を修正しリロード ということは出来ません. 従ってgen-classはAOTでのみ利用可能でJVMの再起動が必要です. deftypeとdefrecord これらでは実際に動的なクラスのリロードが行われます. そのクラスを含むコードをコンパイル場合やforNameが呼ばれた場合など クラス名を解決する必要があるときは,DynamicClassLoader/findClass が呼ばれます....