よくある質問とその答え
ClassNotFoundExceptionとNoClassDefFoundErrorはどうちがうのですか?
技術評論社発行「BEA WebLogic Server 8.1J EJB 2.0徹底攻略」Appendix Bより抜粋
ClassNotFoundExceptionとNoClassDefFoundError−結局どちらも目的のクラスをロードできないということを言っているのに、なぜ2つの違ったクラスがあるのだろう?答えは−クラスがメモリ中にロードされて使えるようになるまでのプロセスにある。ClassNotFoundExceptionはClassクラスのforName()メソッドやClassLoaderクラスのloadClass()メソッドにてスローされる。一方、NoClassDefFoundErrorはJVMからスローされる。
クラスがメモリ中にロードされて使えるようになるためには、クラスローダとJVMは互いに連携して、次の手順を踏む必要がある。
- クラスローダがクラスやインタフェースファイルをロードする
- 実際にJVM上で実行できる形にリンクする。リンクには次の手順が含まれる
- 検証(verification)。そのクラスやインタフェースのバイナリ表現が構造的に正しいかを確認する
- 準備(preparation)。staticフィールドを準備する(staticイニシャライザはこれにはふくまれず、手順3.のクラスの初期化で行われる)
- 解決(resolution)。他のクラスやメソッド参照などで使用されているシンボルを解決する
- クラスやインタフェースを初期化する(その親クラスも)
上記手順の中で、1.においてファイルが見つからないときにクラスローダによってスローされるのがClassNotFoundExceptionだ。そして3.のクラスの初期化手順において、ロードしたクラスに誤りがあって初期化ができないときにJVMによってスローされるのがNoClassDefFoundErrorである。さらに事体を複雑にしているのが、「ロードされたクラスのシンボルが、ロード時にすべて解決されるわけではない」という事実だ。例えばメソッドの中にあるローカル変数である。このローカル変数の型として参照されているクラスは、通常当該メソッドが呼ばれるまでロードされない。したがって、思いもよらないときにNoClassDefFoundErrorに出くわしてあわてる、ということが起こる。
ここで両クラスの継承関係を確認すると:
java.lang.Object java.lang.Throwable java.lang.Error java.lang.LinkageError java.lang.NoClassDefFoundError java.lang.Exception java.lang.ClassNotFoundException
となっている。NoClassDefFoundErrorはJVMよりスローされるので、例外(Exception)ではなくエラー(Error)と分類されている。また、LinkageErrorを継承しており、上記手順2.においてリンクがうまくできなかったときのエラーであることがわかる。LinkageErrorを継承するエラーは実はこれ以外にもあり、例えば検証に失敗したときにスローされるVerifyErrorなどがあるが、本書で取り上げないのは普通の使い方をしていればまずお目にかからないエラーであるからだ。
ClassNotFoundExceptionはExceptionを継承している、いわゆるチェック例外というやつだ。チェック例外はプログラマがcatchブロックにてその例外をハンドルするか、またはさらにその例外をスローしなければいけない。例えば、ClassクラスのforName()メソッドを用いてクラスをロードするプログラムを書きたい場合、次のようにコーディングする:
try{ Class clazz = Class.forName( classname ); } catch( ClassNotFoundException e ){ e.printStackTrace(); }
ClassNotFoundExceptionがチェック例外なのは、プログラマの努力によりクラスのローディングをがんばれる可能性がまだあるからだ。たとえば上記のcatchブロックで、別のクラスローダを使ってクラスをロードすることができるかもしれない。
WebLogic Serverは自分の使えるクラスローダをすべて使ってクラスをロードしようとする。しかし、最終的にファイルが見つからなければClassNotFoundExceptionをスローする。また、JVMはリンクに問題があればNoClassDefFoundErrorをスローする(そしてWebLogic Serverはこれをハンドリングしない)。これら例外とエラーにはこのような違いがある。しかしWebLogic Serverの言っていることは結局は同じだ。「クラスがロードできませんでした、ごめんなさい」。そしてこれらの障害に遭遇したわれわれのとる行動もひとつである。−アプリケーションのパッケージとデプロイを見直すこと、である。