第 4 回 : IM "improve" を実機に載せよう( IM "improve" のダウンサイズ)
はじめに
IM "improve" のクライアントは 40 K バイト強のアプリとなりました。
いろんなサイズ削減の技術によって IM "improve" を小さくします。
- 第 1 回 : 携帯 Java とIM
- 第 2 回 : 仕様・設計と実装( IM "improve" の機能仕様と J2ME/MIDP のつまずきどころ)
- 第 3 回 : 仕様・設計と実装( IM "improve" の設計・実装とエミュレーター動作)
- 第 4 回 : IM "improve" を実機に載せよう( IM "improve" のダウンサイズ)
- 第 5 回 : インターフェイス構築のヒント
- 第 6 回 : IM "improve" の実機対応と携帯電話の将来展望
ダウンサイズと JAR 圧縮
JAR ファイルのサイズ削減には
- 1. クラスファイルのサイズ削減
- 2. JAR ファイルにするときの ZIP 圧縮
の2段階があります。クラスファイルのサイズ削減についてまとめます。
クラスファイルの構造
全体構造
クラスファイルはバイト列で表現されます。
図1 クラスファイルの構造(全体とコンスタントプール)
表1 にそれぞれの簡単な説明を示します。
構造 | 説明 |
---|---|
マジックナンバー | 有名な CA FE BA BE の4バイトです。 |
マイナーバージョン | クラスファイルのフォーマットのバージョン (2バイト) |
メジャーバージョン | クラスファイルのフォーマットのバージョン (2バイト) |
コンスタントプール | クラスファイルのデータ領域。 詳細は下記参照 |
アクセスフラグ | クラスのアクセス制御や性質を示すフラグ (2バイト) |
クラス名 | このクラスを示す CONSTANT_Class のインデックス (2バイト) |
親クラス | 親クラスを示す CONSTANT_Class のインデックス (2バイト) |
インターフェース | このクラスが実装しているインターフェースのリスト |
フィールド | このクラスのフィールド定義 |
メソッド | このクラスのメソッド定義 |
属性リスト | 付加情報など |
コンスタントプール
データ領域であるコンスタントプールについてみていくことにします。図1 の中心にコンスタントプールの詳細構造を示します。 コンスタントプールはコンスタントプールエントリの配列になっていて、クラスファイルのうち 30 % 〜 50 % 以上の容量を占めています。 コンスタントプールエントリは1バイトのタグに続き、エントリごとのデータが続きます。(構造は 図1 右側)
エントリは 11 種類あります。表 2 にエントリ一覧を示します。
分類 | タグ(10進数) | エントリ | 説明 | 参照先 |
---|---|---|---|---|
単純データ型 | 1 | Utf8 | Utf8 でエンコードされた文字列 | - |
3 | Integer | int リテラル (32bit 整数) | - | |
4 | Float | float リテラル (32bit 浮動小数点) | - | |
5 | Long | long リテラル (64bit 整数) | - | |
6 | Double | double リテラル (64bit 浮動小数点) | - | |
参照型 | 7 | Class | クラスまたはインターフェースへの参照 | Utf8 |
8 | String | String リテラル | Utf8 | |
9 | Fieldref | フィールド参照 | NameAndType | |
10 | Methodref | メソッドへの参照 | NameAndType | |
11 | InterfaceMethodref | インターフェースへの参照 | NameAndType | |
12 | NameAndType | メソッド・フィールド参照で名前と型を定義するもの | Utf8 |
参照型エントリの参照をたどれば、最終的には文字列 Utf8 へたどりつきます(表 2)。 参照型エントリと数値データ型エントリはサイズが固定のためサイズ削減できません。メソッドやフィールドの名前として使わているだけのものは、 定義と参照に整合性があれば短くできます。
エントリの短縮と共有
簡単な例でコンスタントプールのサイズ削減を説明します。
図2 短縮と共有の模式図
図2 の「短縮前」を見てください。2つのエントリ NameAndType が それぞれ Utf8 の "Sophia" と "Cradle" を参照していたとします。 もしこの "Sophia" と "Cradle" が変更可能であった場合、たとえば1文字で "a" に変更します。(図2 「短縮後・共有前」)
ここで、エントリ "a" が2つ存在しますので、共有して、図2 「共有後」 のようにします。
プログラムで表示している文字列や型名をフィールド名やメソッド名にすることも可能です。(「ダウンサイズの実際」その 9 をご覧ください。)
Java VM の仕組みとインストラクション
Java VM がインストラクションを実行する概要を説明します。
ローカル変数配列とオペランドスタック
Java VM ではプログラム実行時には、スレッドごとに Java スタックが生成され、 そこにメソッド呼出ごとにフレームが積み上げられていきます。
フレームの中には、ローカル変数領域と、オペランドスタック領域があります。(図3 参照)
図3 Java スタックの構造
Java VM のローカル変数は Java 言語のローカル変数と同様に、 メソッドの実行中にだけ存在する値の格納場所ですが、 名前ではなくインデックスで指定され、 インスタンスへの参照・メソッド引数・( Java 言語での)メソッドのローカル変数などを 保存するために使用します。
オペランドスタックはインストラクションの引数・返値用の一時保存場所で、 値を積み上げてインストラクションを実行すると、引数として積み上げた値は取り出され、 結果が積み上げられます。
インストラクションの機能分類
インストラクションを分類して概要を示します。型や細かい挙動でバリエーションが多くあるものは、「系」として分類しています。
大分類 | 機能 | インストラクション |
---|---|---|
スタック | 定数をスタックに積む | push系, ldc系, const系 |
スタックの編集 | nop, pop系, dup系, swap | |
ローカル変数 | ローカル変数から取り出した値を スタックに積む |
load 系 |
スタックから pop した値を ローカル変数に格納する |
store 系 | |
その他 | iinc, wide | |
配列 | 配列の生成 | newarray, anewarray, multianewarray |
配列から値を取り出す | aload系 | |
配列に値を格納する | astore系 | |
その他 | arraylength | |
オブジェクト | 新規生成 | new |
フィールドへのアクセス | putfield, getfield, putstatic, getstatic | |
その他 | checkcast, instatnceof |
機能 | インストラクション |
---|---|
算術演算 | add系, sub系, mul系, div系, rem系, neg系 |
論理演算 | sh系, and系, or系, xor系 |
型変換 | 2系 |
大分類 | 機能 | インストラクション |
---|---|---|
実行位置の移動など | 条件分岐 | if系 |
比較 | cmp系 | |
無条件分岐とサブルーチン | goto系, jsr系, ret系 | |
テーブルジャンプ | lookupswitch, tableswitch | |
メソッド | 呼び出し | invokevirtual, invokespecial, invokestatic, invokeinterface |
リターン | retrun系 |
機能 | インストラクション |
---|---|
例外 | athrow |
デバッグ | breakpoint |
モニタ | monitorenter, monitorexit |