Wicketアプリが重くて遅い
Wicketで作成したアプリが重くて遅く、とても実用にならない場合がある。これはどんな場合かというと、
- アプリケーションをスタンドアロンのjarとしてまとめ、それを実行したとき。つまり、アプリそれ自体に例えばJettyを内蔵させてスタンドアロンのアプリケーションとして実行した場合。
確認していないが、おそらくwarファイルの場合はあまり問題が出ないものと思われる(遅くなる可能性があるが)。また、例えばEclipse上でのそのアプリを開発中の場合は問題が顕在化しない。これはなぜかというと。
- プログラムをjarファイルの形にまとめた時にだけ問題が現れる。
からである。warファイルも同じようなものであるが、サーブレットコンテナ上で実行される場合は、それが展開された形になる(たしか)ので、問題が現れない。
現象
以下は、あるアプリを数分間動作させ、数枚のページ遷移を実行したものである(VM引数として-Xloggcをつけ、gcviewerで表示)。 すぐにデフォルトの64Mを使いきってしまうことがわかる。もちろんアプリの動作は非常に重く、操作を続行するとメモリ不足が起こる場合もある。
また、ここには示さないが、このときの状態をEclipse Memory Anlyzerを使って観察してみると、 「sun.net.www.protocol.jar.URLJarFile」というオブジェクト、あるいはそのfinalizeをするためのFinalizerに埋め尽くされていることがわかる。
http://www.docjar.com/html/api/sun/net/www/protocol/jar/URLJarFile.java.html
何らかの理由でURLJarFileというオブジェクトが大量発生するのだが、このクラスにはfinalizeが使われている。finalize付のオブジェクトの場合、そう簡単に廃棄されてはくれない。一説によると、二回のGCが必要とのことである。
このため、不要になってもメモリ上に残ってしまいアプリのメモリを圧迫する。また、そもそもこのようにメモリを使うオブジェクトを大量に作成すること自体が問題である。
原因
Wicketのコードをまだ完全には追いきれていないのだが、これはWicketがリソースを取得する際に発生する。主にはXXXPage.javaに対応するXXXPage.htmlというファイルである。
これらのファイルが、OSのファイルシステム中にある場合(warファイルが展開された状態や、Eclipse上での開発途中)では、Wicketは単純にそのファイルを読み込む。これに対して、Jarファイルの中に埋め込まれている場合には、WicketはそのJarファイルをまるまるメモリにロードしてしまうようである。その際に、sun.net.www.protocol.jar.URLJarFileを使用している模様である。
もちろん、これはWicketが意図的にやっていることではなく、Wicketの作業の途中でJava-APIを呼び出すと結局、個々のリソース取得のたびに「sun.net.www.protocol.jar.URLJarFile」が使用されてしまう模様である。
また、これは開発者側が作成したXXXPage.htmlというファイルに限らず、wicketの配布jarファイル中にあるFeedbackPanel.htmlなどのリソースにも適用されてしまう。したがって、仮に開発アプリにhtml等のリソースが一つもなくとも、あるいは開発アプリをjarにまとめなくとも、wicketの配布jarをそのまま使う限りは必ず発生する。