[Home] [Kuri] [Sysad] [Internet?] [Blog] [Java] [Windows] [Download] [Profile] [Flash] [+]

クラスファイルを小さくする

概要

クラスファイルの中には、実行に直接必要のない情報が含まれています。 ここでは、属性の中の、ソースファイル名と(ソースの)行番号情報を削る ことで、クラスファイルのサイズを小さくする方法について説明します。
ちなみに、これを行うことで多少サイズが小さくなりますが、それに見合う メリットがあるかどうかはよくわかりません。(^__^;;
# ちなみにデメリットは、exception が発生した時に stack trace を出力 しても、ソースの行番号が出力されないことです。(他にもあるかも…。)

JavaClass

JavaClass は、クラスファイルを解析したり生成したり編集したりするための API を提供してくれます。
簡単なところでは、クラスファイルの情報を以下のようにして表示させる ことができます。

import java.io.IOException;
import de.fub.bytecode.classfile.*;

public class dump {
    public static void main(String[] args) {
	if (args.length != 1) {
	    System.err.println("Usage: (java) dump inputclass");
	    System.exit(1);
	}

	try {
	    ClassParser parser = new ClassParser(args[0]);
	    JavaClass jclass = parser.parse();
	    System.out.println(jclass);
	} catch(IOException e) {
	    e.printStackTrace();
	}
    }
}

ここでは、まず、クラスファイルを解析するために、ClassParser のインス タンスを生成します。次に、メソッド parse() によって、JavaClass の オブジェクトを得ます。
JavaClass から、フィールドやメソッド、属性などの情報がすべて得られ ますし、変更することもできます。ですので、操作したいクラスファイル の JavaClass のオブジェクトさえあれば、解析や編集を比較的簡単に行う ことができます。
ここでは、単に JavaClass を表示しています。

また、コンスタントプールを表示させるには、以下のようにします。

import java.io.IOException;
import de.fub.bytecode.classfile.*;

public class cp {
    public static void main(String[] args) {
	if (args.length != 1) {
	    System.err.println("Usage: (java) cp inputclass");
	    System.exit(1);
	}

	try {
	    ClassParser parser = new ClassParser(args[0]);
	    JavaClass jclass = parser.parse();
	    ConstantPool cp = jclass.getConstantPool();
	    int len = cp.getLength();
	    for(int i=1; i<len; i++) {
		System.out.println(i + " : " + cp.getConstant(i));
	    }
	} catch(IOException e) {
	    e.printStackTrace();
	}
    }
}

JavaClass のオブジェクトを得た後、getConstantPool() メソッドでコンス タントプールを得ています。あとはそれらを1つ1つ表示しています。

本番

では、ソースファイル名と行番号情報を削るものを作ってみましょう。
まずは、前節と同様に JavaClass のオブジェクトを得ます。次に、クラス の属性から、ソースファイル情報を探して省きます。そして、各メソッド の属性から、行番号情報を探して省きます。

import java.io.IOException;
import de.fub.bytecode.Constants;
import de.fub.bytecode.classfile.*;


public class strip implements Constants {
    public static Attribute[] stripAttributes(Attribute[] attr) {
	boolean[] res = new boolean[attr.length];
	int total=0, i=0;

	/* search unnecessary attributes */
	for(int j=0; j<attr.length; j++) {
	    byte tag = attr[j].getTag();
	    res[j] = (tag != ATTR_SOURCE_FILE&&tag != ATTR_LINE_NUMBER_TABLE);
	    if(res[j]) total++;
	}

	/* remake and return attributes */
	Attribute[] ret = new Attribute[total];
	for(int j=0; j<attr.length; j++) {
	    if(res[j]) {
		ret[i++] = attr[j];
	    }
	}
	return ret;
    }

    public static void main(String[] args) {
	try {
	    if (args.length != 2) {
		System.err.println("Usage: (java) strip inputclass outputclass");
		System.exit(1);
	    }

	    /* strip class attributes */
	    ClassParser parser = new ClassParser(args[0]);
	    JavaClass jclass = parser.parse();
	    Attribute[] attr = jclass.getAttributes();
	    jclass.setAttributes(stripAttributes(attr));

	    /* strip code attributes */
	    Method[] meth = jclass.getMethods();
	    for(int i=0; i<meth.length; i++) {
		Code code = meth[i].getCode();
		attr = code.getAttributes();
		code.setAttributes(stripAttributes(attr));
	    }

	    /* dump stripped class */
	    jclass.dump(args[1]);
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }
}

クラスメソッドである stripAttributes() が、与えられた属性から、 ソースファイルか行番号以外の属性を新たに作成して戻します。 あとは、setAttributes() メソッドを使って(小さくした)属性を設定し 直し、dump() メソッドでファイルに書き出しています。
この調子だと、他の方法でさらに小さくできそうな気がします。
(この続きは その2(まだ構想段階) へ…。)

Powered by Apache PostgreSQL Usupi Logo Kuri Logo
[Home] [Kuri] [Sysad] [Internet?] [Blog] [Java] [Windows] [Download] [Profile] [Flash] [-]
usu@usupi.org Last modified : Wed Jun 6 14:34:44 2001