ローカルなバッファ

またJavaかよ・・・。
というわけで。今回はメソッドローカルなバッファについて。

まー、実際にコードを組んでると、計算用に配列なりオブジェクトなりが必要だったりします。ところが、Javaではスタック上に配列やオブジェクトを確保できません。そりゃまー値返したり、ポインタをオブジェクトに登録したりするしね。メソッド抜けてメモリが開放されたら困るわけです。たとえば、

public void function(...){
    ....
    int [] buffer = new int[300];
    ....
}

みたいなかんじ。

ところが、速いコード書きたい場合はこれがよろしくないわけです。メソッド呼ぶたびにnewすると、まー1000000回くらい回せばGCのお世話になるでしょー。そんなに回さないときには、このままで十分。
で、もし1000000回くらい回すときはどうするかというと、バッファを使いまわす。どうせ、メソッドから返るとバッファ要らなくなる場合とかよくあるしね。するとGCのお世話にならずにすみそうな感じ。

private final int [] buffer = new int[300];
public void function(...){
    ....
    int [] buffer = this.buffer;
    ....
}

で、今インスタンス変数なわけですが、別にクラス変数でもいいわけです。別に情報を保持してるわけじゃないですから。というわけで、

public class ClassA{
    private static final int [] buffer = new int[300];
    public void function(...){
        ....
        int [] buffer = ClassA.buffer;
        ....
    }
}

staticをつける。これでクラスのインスタンス毎に無駄なバッファが作られることもないわけです。

で、ところがこれはマルチスレッドにした瞬間に破綻するんですねぇ。で、どうするか。

そんなときに登場するのがThreadLocalクラス。これは、スレッドごとに異なったオブジェクトを返してくれるクラスです。使い方は、

public class ClassA{
    private static final ThreadLocal<int []> buffer = new ThreadLocal<int []>(){
        protected int [] initialValue(){
            return new int[300];
        }
    };
    public void function(...){
        ....
        int [] buffer = ClassA.buffer.get();
        ....
    }
}

見たいな感じ。initialValueメソッドをオーバーライドしてやればいいわけです。使うときはgetを呼ぶ、と。これで、マルチスレッド対応版の完成。

で、まぁ、もちろん適材適所で、最初のヤツはほとんど呼び出されないようなメソッドに有効。何度も呼び出される場合に限り、シングルスレッドの場合はstaticまでつけたバージョン、マルチスレッドの場合はThreadLocalを使ったバージョン、といろいろ使い分けるのがいい感じ。