SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Javaファイナライズのメモリ保持問題への対処方法

JVMのファイナライズ処理の仕組みとテクニック

サブクラス作成時のメモリ保持問題を回避する

 ファイナライズを明示的に使っていない場合でも、リソースの再生に遅延が生じる可能性があります。次に例を示します。

public class RGBImage1 extends Image1 {
    private byte rgbData[];
}

 RGBImage1Image1の継承クラスで、フィールドrgbData(およびこの例では示されていない一部のメソッド)を継承します。RGBImage1でファイナライザを明示的に定義しなかったとしても、このクラスはImage1からfinalize()メソッドを自然に継承するので、RGBImage1のインスタンスもすべてファイナライズ可能と見なされます。RGBImage1のインスタンスが到達不能になった場合、このインスタンスに含まれるrgbData配列は、インスタンスがファイナライズされるまで回収されません(図3を参照)。rgbData配列は非常に大きくなることもあるので、この配列の回収が遅れるとリソースの使用効率が低下します。ファイナライザが深いクラス階層の中に「隠されている」場合、この問題の原因を見つけるのはなかなか困難です。

図3 インスタンスがファイナライズされるまでrgbData配列は回収されない
図3 インスタンスがファイナライズされるまでrgbData配列は回収されない

 この問題を回避する1つの方法は、次のように、「継承」パターンではなく、「包含」パターンを使うようにコードを修正することです。

public class RGBImage2 {
    private Image1 img;
    private byte rgbData[];

    public void dispose() {
        img.dispose();
    }
}

 RGBImage1と異なり、RGBImage2Image1の継承クラスではなく、Image1のインスタンスを包含しています。RGBImage2のインスタンスが到達不能になると、ガベージコレクタは直ちにRGBImage2のインスタンスとrgbData配列(他のオブジェクトから到達不能になっている場合)を回収し、Image1インスタンスだけをファイナライズキューに追加します(図4を参照)。クラスRGBImage2Image1のサブクラスではないため、メソッドを継承しません。したがって、RGBImage2には、Image1の必須メソッド(たとえばdispose()メソッド)にアクセスする委譲メソッドを追加する必要があります。

図4 ガベージコレクタはImage1インスタンスだけをファイナライズキューに追加する
図4 ガベージコレクタはImage1インスタンスだけをファイナライズキューに追加する

 しかし、必ずしも前述のようにコードを修正できるとは限りません。修正できない場合は、クラスのユーザーとして、インスタンスがファイナライズ時に必要以上のリソースを消費しないようにもう少し工夫する必要があります。次のコードを参照してください。

public class RGBImage3 extends Image1 {
    private byte rgbData[];

    public void dispose() {
        super.dispose();
        rgbData = null;
    }
}

 RGBImage3は、基本的にはRGBImage1と同じですが、dispose()メソッドが追加されているという点が異なります。このdispose()メソッドでは、rgbDataフィールドをnullにします。RGBImage3インスタンスの使用後にdispose()を明示的に呼び出し、rgbData配列を速やかに回収する必要があります(図5を参照)。フィールドを明示的にnullにするのはあまりお勧めできませんが、これは例外的なケースです。

図5 RGBImage3インスタンスの使用後にdispose()を呼び出す
図5 RGBImage3インスタンスの使用後にdispose()を呼び出す

メモリ保持問題からユーザーを解放する

 前節では、ファイナライザを使うサードパーティ製クラスを利用するときのメモリ保持問題の回避方法について説明しました。今度は、自分でクラスを開発する場合を想定し、ユーザーにこのような手間をかけさせないために、事後クリーンアップを要求するクラスの作成方法を考えてみましょう。一番よい方法は、クラスを2つ(事後クリーンアップの必要なデータを格納するクラスと、それ以外のデータを格納するクラス)に分け、前者に対してのみファイナライザを定義する方法です。具体的な例を次に示します。

final class NativeImage2 {
    // pointer to the native image data
    private int nativeImg;

    // it disposes of the native image;
    // successive calls to it will be ignored
    private native void disposeNative();
    void dispose() { disposeNative(); }
    protected void finalize() { dispose(); }
}

public class Image2 {
    private NativeImage2 nativeImg;
    private Point pos;
    private Dimension dim;

    public void dispose() { nativeImg.dispose(); }
}

 Image2は、Image1と似ていますが、nativeImgフィールドを別のクラスNativeImage2内に格納しています。そのため、Image2クラスからnativeImgにアクセスするには、間接的な手段を取る必要があります。しかし、Image2インスタンスが到達不能になった場合は、NativeImage2インスタンスだけがファイナライズキューに追加され、Image2インスタンスから到達可能なものはすべて速やかに回収されます(図6を参照)。クラスNativeImage2finalと宣言されているので、ユーザーはNativeImage2のサブクラスを作成できず、前述のメモリ保持問題が再現されることはありません。

図6 Image2インスタンスが到達不能になった場合はNativeImage2インスタンスだけがキューに追加される
図6 Image2インスタンスが到達不能になった場合はNativeImage2インスタンスだけがキューに追加される

 細かいことを言えば、NativeImage2Image2の内部クラスであってはなりません。内部クラスのインスタンスは、作成元である外部クラスのインスタンスの暗黙的参照を持ちます。したがって、NativeImage2Image2の内部クラスで、NativeImage2インスタンスがファイナライズキューに追加された場合は、対応するImage2インスタンスも保持されることになります。これは、まさに回避しようとしている問題です。しかし、NativeImage2クラスがImage2クラスからのみアクセス可能ならどうでしょうか。NativeImage2クラスにパブリックメソッドを用意していないのはこのためです(クラスだけでなくdispose()メソッドもパッケージ内でのみ利用可能です)。

次のページ
ファイナライズの代替手段

修正履歴

この記事は参考になりましたか?

japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Tony Printezis(Tony Printezis)

SunLabsでの3年以上の勤務を経て、Sun MicrosystemsのJava HotSpot Virtual Machine開発チームに参加。主に、ガベージコレクタのスケーラビリティ、反応性、並列性、ビジュアル化を中心に、動的メモリ管理に携わる。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/337 2006/04/11 19:12

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー