MASATOの開発日記


前の開発日記 次の開発日記 一覧

2003/07/22

C++とJavaとC#のどれを使うかと言う話は私の中ではそろそろ決着が付きそうです。

Webサービスサーバーアプリケーションを開発するときはJava。 その理由はマルチプラットフォームに対応していることです。 C++やC#も、単一プラットフォームにしか対応していない言語ではないのですが、 Javaに比べると問題が多すぎです。

Webサービスクライアントアプリケーションや、ユーティリティアプリケーション、ゲームアプリケーションを開発するときはC++。 決め手になったのは、C#で作成したGUIアプリケーションが軽快に動かなかったことと、 C++向けWebサービスライブラリが存在することが分かったことです。 つまりC++でも、ちょっとだけ余計に苦労するだけでC#よりずっと軽快なアプリケーションが作れるわけです。

というわけで私はしばらくC#とはおさらばし、C++とJavaだけを使うことにしました。 C#で軽快なアプリケーションを作れるようになるか、マイクロソフトが本気で.Netフレームワークのマルチプラットフォーム対応を 目指すようになったら再びC#を使うことを検討してみたいと思います。

WeakReferenceを使ったHashMap

環境Java2 1.4 Standard Edition

以下の特徴を持ったアプリケーションについて考えて見ます。

例えば、住所録アプリケーションや、ゲームアプリケーションなどの一部は、このような特徴を持っています。

このようなアプリケーションにおいては、大概、読み込んだデータに対して処理を行った後、 読み込んだデータはメモリ上に置いておく必要が無くなります。 そのため、適当なタイミングでメモリ上から破棄していくことになります。

Javaで、このような読み込み処理と破棄処理をどのように実装するか考えて見ます。 データファイルから読み込んだデータはDataFileクラスのインスタンス格納することにします。 DataFileクラスがどのようなクラスであるかは考えないことにします。 1つのデータファイルに対し、1つのDataFileクラスのインスタンスが生成されるようにします。 2つ以上生成すると、メモリの無駄ですし、ファイル更新処理の同期を取るのも大変であるためです。 そこで、DataFileクラスのインスタンスを、ファイル名をキーとしたHashMapクラスを使って管理することにします。

読み込み処理は、以下のような実装になります。

import java.io.*;

public class DataFileManager {
  private HashMap map = new HashMap();
  DataFile get(File file) {
    if (map.containsKey(file)) {
      // ファイルが既に読み込まれている場合
      return (DataFile)map.get(file);
    }else {
      // ファイルがまだ読み込まれていない場合
      DataFile data = new DataFile(file);  // fileに対応したDataFileクラスを生成する
      map.put(file, data);
      return data;
    }
  }
}

さて、読み込み処理はこれで良いとして、破棄処理はどうすれば良いのでしょうか?

ここからが本題です。前置き長すぎ。
こういうときは、管理用クラスでは、WeakReferenceクラスを使って、 DataFileへの弱参照を保持するようにしておくと良いです。 弱参照だけ保持すれば、DataFileのインスタンスが他の部分で使われなくなったとき、 GCの対象となって回収されます。

DataFileクラス用に限らず、汎用的に使えるようにしたWeakMapクラスを以下に記述します。

import java.lang.ref.*;
import java.util.*;

public abstract class WeakMap {
  private HashMap map = new HashMap();
  public Object get(Object key) {
    synchronized (map) {
      Object o;
      if (!map.containsKey(key)) {
        o = load(key);
        map.put(key, new WeakReference(o));
        return o;
      }else {
        WeakReference ref = (WeakReference)map.get(key);
        o = ref.get();
        if (o == null) {
          o = load(key);
          map.put(key, new WeakReference(o));
        }
      }
      return o;
    }
  }
  protected abstract Object load(Object key);
}

ちゃんとマルチスレッドにも対応しております。

実際に使うときは、WeakMapクラスを派生させ、loadメソッドを実装した次のようなクラスを作る必要があります。

class WeakMapTestImpl extends WeakMap {
  protected Object load(Object key) {
    // keyからオブジェクトを構築して返す。
  }
}

WeakMapを使う側も、getメソッドで取得したインスタンスが自然にGCの対象になるようになっていれば良いので、 使う側の負担も少ないです。

WeakReferenceではなくSoftReferenceを使うと、 直ぐには回収しないけれどメモリが足りなくなる前には回収するような制御になるらしいです。 java.lang.refパッケージは、奥が深そうです。

前の開発日記 次の開発日記 一覧