Java: Pack200 で超圧縮

J2SE 5.0 で導入された、Pack200 というのがある。これは、Jar ファイルをさらに圧縮するためのファイルフォーマットで、JSR-200 で仕様が決定された。詳しい説明は Sun のドキュメントに書かれている。

Pack200 and Compression
http://java.sun.com/j2se/1.5.0/docs/guide/deployment/deployment-guide/pack200.html

以下は、上記ドキュメントの要約。
まず、Java のクラスファイルを配布する時のフォーマットといえば、今までは Jar しかなかった。しかし、圧縮率がイマイチで、ネットワーク経由での配布なんかだと、まだまだ「なにこの Jar ファイルちょーでかーい!!」という場面もあるはず。
そこで、JDK 5.0 に含まれている pack200 コマンドで、Jar ファイルを圧縮する。

$ pack200 foo.jar.pack.gz foo.jar

引数は、1 つ目が圧縮後の名前、2 つ目が圧縮前の名前となる。ご覧の通り、Pack200 で圧縮した後に、gzip でさらに圧縮してくれる。大きいファイルほど効果が大きいそうだが、試しに JDK 5.0 の lib/tools.jar を圧縮してみたところ、6.6MB あったのが、なんと、わずか 800KB ちょっとに。
元に戻す時は、同じく JDK 5.0 に含まれている unpack200 コマンドを使う。引数はさっきと同じ。

$ unpack200 foo.jar.pack.gz foo.jar

脅威の圧縮率、その秘密は、Jar の中身に注目したアルゴリズム。Jar ファイルの中にあるクラスファイルを解析し、コンスタントプールを一箇所にまとめているのだ。
ネットワーク経由で配信する時には、HTTP の Accept-Encoding/Content-Encoding を使って、変換後の Jar ファイルをやりとりすることができる。ちなみに、上記のドキュメントによれば、転送に使ってよい形式は以下の 3 種類。

  • foo.jar (元の形式)
  • foo.jar.gz (gzip のみ)
  • foo.jar.pack.gz (Pack200+gzip)

ただし、pack200/unpack200 コマンドでは、

  • foo.jar.pack (Pack200 のみ、gzip はしない)

という形式も扱えるようになっている。これは、ネットワーク転送用じゃなくて、Pack200 をインストーラなんかで使いたい人のためのものらしい。インストーラには独自の圧縮技術が載っていることがあるので、そういうのが既にある人は、foo.jar.pack をその技術で圧縮すればいい。
例えば、Windows 系のインストーラの場合なら、インストールしたい各 Jar ファイルを foo.jar.pack、bar.jar.pack といった形に圧縮した後、Cab 形式でまとめて圧縮する、といった形がとれる。Pack200 のアルゴリズムは、Jar 特化なだけあってかなり性能がいいので、Jar をそのまま Cab でまとめるよりも、インストーラのサイズが小さくできることになる。
[2004-10-21 追記] Pack200 の圧縮・伸長は、Java プログラムからも行える。使い方については、java.util.jar.Pack200 クラスのドキュメンテーションに載っている。