JavaVMの上にJavaVM?
前の節で述べたような各種の制約を乗り越える方法について考えてみましょう。たとえば、あらたなスクリプト言語を設計して、その実行エンジンを開発する、ということが考えられます。スクリプト言語の設計として他のスクリプトを読み込めるようにすれば、クラスローダが無いことからくる制約はなくなります。また、実行エンジンにデバッグ機能を設けるようにすればデバッグも楽になるでしょう。アプリサイズについても、データ領域をスクリプト置き場に使えますし、ネットワークからスクリプトをダウンロードしたりできますので、ずいぶんと楽になります。
しかし、スクリプト言語の設計と実装というのはなかなか簡単なことではありません。まずはどのような機能を持たせるか検討して絞り込み、文法を考えてパーサを書き、実行エンジンを実装し、ライブラリを整備する必要があります。使いやすいものを作ろうとするとかなりのスキルとバランス感覚が要求されるでしょう。さらに、それを使う開発者もあらたな言語を覚えなければならないという学習コストがかかります。
一方、世の中にはすでにJava があります。Java の言語は良く設計されており、汎用的で、実績もあり、また、ライブラリも関連ツールも揃っています。クラスファイルのフォーマットも決まっていますので、あとはそれをパース/実行するエンジンを作るだけで済みます。そうして、クラスファイルをロードする機能を追加し、デバッグ機能を設ければ、前述のようなあらたなスクリプト言語を設計するのと同様のことができるようになります。さらに、開発者も新たな言語を覚える必要がありません。
このように、ネイティブのJavaVMの上にさらにJavaVMを載せるというのは一見冗長に見えますが、実は多くの利点があることがおわかり頂けるのではないかと思います。今回、私達はこのような考えのもとにJ/byteEngine技術を開発しました。

バイトコードのインタプリタ
J/byteEngineのコア部分は、バイトコードインタプリタです。基本的な動作はJavaのVMと同じで、クラスファイルをパースして、メモリ上に展開して、バイトコードを逐次実行していきます。JavaのVMはスタックマシンなので、インタプリタそのものの実装は比較的シンプルにできます。また、J/byteEngineそのものがJavaで書かれているので、メモリ管理はネイティブのVMに任せることができるというのも実装の容易さにつながっています。ケータイ上のネイティブのVM と比べたときの違いは、ベリファイアを搭載していないということです。すなわち、不正なバイトコードであってもJ/byteEngineの上でなら実行できてしまう可能性があります。とは言っても、そのJ/byteEngineそのものはケータイ上のネイティブのVM上でベリファイされた後に実行されているので、不正なバイトコードを実行したからといってケータイに対して何か悪さができるというわけではありません。
APIのマッピング
J/byteEngineのインタプリタ上でメソッドを呼び出すにあたって、アプリのクラスのメソッドを相互に呼び出している分にはとくに問題ありません。しかし、アプリがjava.lang.Stringやcom.nttdocomo.ui.IApplicationといったクラスのメソッドを呼び出そうとした場合は、ネイティブのVM のメソッドを呼び出してやる必要があります。そして、そのためにはネイティブのVMが生成したインスタンスが必要になります。そこで、J/byteEngineではバイトコードを実行するなかでインスタンスをnew する際にはネイティブのVM側のインスタンスを生成します。
そうすれば、ネイティブのAPIも自由に呼び出すことができます。ただ、J2SEと違ってCLDCにはリフレクションAPIが存在しないため、J/byteEngineからネイティブのVMのメソッドを呼び出すためには,メソッドを呼び出すためのコードがJ/byteEngineのなかに必要になります。ネイティブのVMが持っているすべてのメソッドについてコードを持っておくのは無駄なので、実際には必要なメソッドのコードのみを自動生成するしくみを採用しています。
J/byte Engine の制約
アプリはJ/byteEngineの上で実行されるため、基本的にJ/byteEngineの制御下にあります。
しかし、前述のAPIのマッピングとの関係もあって、現状の実装ではアプリがクラスをロードする時にはネイティブのVMとJ/byteEngineと両方でクラスをロードしています。そして、インスタンスはネイティブのVM側で生成します。そのため、コンストラクタの実行についてはJ/byteEngineの制御から外れてしまいます。
また、後からネットワーク経由でダウンロードしたクラスはネイティブのVMでクラスをロードすることができません。そのため、メソッドを呼び出したり参照を渡したりといったことはJ/byteEngine上のコードのなかでしかできません。たとえば、ネイティブのVM側のメソッドを呼び出す際にそのクラスのインスタンスを引数に渡す、というようなことはできなくなっています。
クラスの動的ロードが可能
さて、J/byteEngineの上でアプリを実行するメリットにはどのようなものがあるでしょうか。
1つには、クラスの動的なロードが可能だということです。ここでいう動的とは、最初にアプリ(Jarファイル)をダウンロードした後に、データ領域などから必要に応じてクラスをロードすることを指しています。まず、データ領域からクラスをロードできるようになったことで、これまではJarファイルのサイズに縛られていたコードサイズの制限を乗り越えることができます。また、サーバからデータ領域にクラスファイルをダウンロードすることもできますから、アプリの不具合を修正したり、機能追加を行ったりすることができます。
実行制御が可能
J/byteEngineのインタプリタは、ネイティブのVM から見るとアプリとして作られています。
すなわち、バイトコードの実行をネイティブ側ではなくアプリ側で制御できるということです。したがって、インタプリタに手を入れればステップ実行やブレークポイントの設定などといったデバッグ機能を実装することができます。デバッグの基本は、まずプログラムが実際にプラットフォーム上でどのように実行されているかを把握することですから、J/byteEngineというのは強力なデバッグツールとなり得るものだと言えます。
性能への影響
一方、J/byteEngineの上でアプリを実行することのデメリットもいくつかあります。いちばんわかりやすいデメリットは、バイトコードの実行性能の低下です。J/byteEngineのインタプリタもJavaで書かれていて、その上でバイトコードを実行するわけですから、単純に考えても数倍〜数十倍の時間がかかりそうです。しかし、実際にアプリを動かしてみると体感ではそれほどの性能低下は感じられません。もちろん、複雑な計算や暗号化などといった処理をやっていれば、純粋にバイトコードの実行性能そのものが表れてくるの
で性能低下も顕著でしょうが、実際のアプリではネイティブのAPI を呼び出して処理している部分が結構な割合になります。ネイティブのAPIは従来どおりの速度で実行されますので、トータルとしての性能低下は意外と小さいもので済むのです。
メモリへの影響
メモリに関しても、J/byteEngine上でアプリを実行すると、ネイティブのVM で実行するのと比較して少しばかり多めにメモリを消費します。
まずはJ/byteEngineのインタプリタそのもののクラスファイルが必要になります。また、アプリのクラスについてはJ/byteEngine側でもクラスファイルをパースして展開しますし、その実行にあたってはスタックやフレームのためのメモリが必要になります。アプリにもよりますが、数10Kバイト〜数100Kバイトといったところでしょう。最近のケータイであればヒープメモリも数Mバイト搭載されていますので、J/byteEngineによる影響はとくに大きなものではないと言えるのではないでしょうか。