ガーベッジコレクション

<概要>メモリ解放の仕組みについて学びます。

例題15-1 インスタンスを生成し続けて、ガーベッジコレクションを発生させる。

○プログラム

public class GcTest1{
  public static void main(String[] args){
    while(true){
      String s=new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());
    }
  }
}


○実行結果

……
残りメモリ=1508208
残りメモリ=1507992
残りメモリ=1507776
残りメモリ=1507560
残りメモリ=1507344
残りメモリ=1936864
残りメモリ=1936648
残りメモリ=1936432
残りメモリ=1936216
残りメモリ=1936000
……

○解説

ガーベッジ( garbage )というのは、
誰からも参照されていない(使用されることがない)インスタンスのことです。
「garbage」は「ゴミ」を表す英語です。

ガーベッジコレクション( garbage collection )というのは、
誰からも参照されていないインスタンス(すなわちガーベッジ)が占めているメモリ領域を回収し、
再利用することです。
ガーベッジコレクションを行う機構のことをガーベッジコレクタ( garbage collector )と呼びます。
コレクション( collection )は集めること、コレクタ( collector )は集める人のことです。

4. String s=new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

s は String 型の変数です。
インスタンスではありません。
インスタンスはプログラムでは表現されないけれど、メモリのどこかにある実体、データです。
そして、それを指すポインタ参照型の変数になります。
変数とインスタンスは混同しやすいですが、違いの分かるプログラマになりましょう(笑)!

5. System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());

Runtime.getRuntime() は、現在のランタイム(動作環境)を表すインスタンスを得るクラスメソッドです。
freeMemory() は、空きメモリ量を得るメソッドです。

さて、プログラムは無駄にゴミを生成していき、
残りメモリはだんだんと減りますが、あるとき突然残りメモリが増えています。
このとき、ガーベッジコレクションが発生しているのですね。

○補足

インスタンスが確保されているメモリ領域のことをヒープ( heap )と言います。

○補足2

String s=new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

は、次のように書いても同じです。

String s="ABCDEFGHIJKLMNOPQRSTUVWXYZ";

public final class String extends Object
implements Serializable, Comparable, CharSequence
  //java.lang パッケージ
 String クラスは文字列を表します。
 Java プログラム内の "abc" などのリテラル文字列はすべて、このクラスのインスタンスとして実行されます。
 文字列は定数です。
 この値を作成したあとに変更はできません。

public String(String original)  //String クラス ← java.lang パッケージ
 新しく生成された String オブジェクトを初期化して、引数と同じ文字シーケンスを表すようにします。
 つまり、新しく作成された文字列は引数文字列のコピーになります。

Runtime クラスのメソッド

public class Runtime extends Object  //java.lang パッケージ
 Java アプリケーションはすべて、Runtime クラスの単一のインスタンスを持ちます。
 このクラスは、アプリケーションとアプリケーション実行環境とのインタフェースになります。

public long freeMemory()  //Runtime クラス ← java.lang パッケージ
 Java 仮想マシン内の空きメモリの量を返します。
 gc メソッドを呼び出すと、freeMemory によって返される値が増える場合があります。
戻り値: 将来割り当てられるオブジェクトに利用可能な現在のメモリの総容量 (バイト単位)

public void gc()  //Runtime クラス ← java.lang パッケージ
 ガベージコレクタを実行します。
 このメソッドを呼び出すと、Java 仮想マシンは使用していないオブジェクトをリサイクルし、
 使用中のメモリをすばやく再利用可能な状態にします。
 メソッド呼び出しから制御が戻された時点で、
 仮想マシンは破棄されたオブジェクトをすべて再利用するよう最善を尽くしたことになります。
 gc というメソッド名は「garbage collector」の頭字をとったものです。
 gc が明示的には呼び出されなかった場合でも、
 仮想マシンはこの再利用プロセスを必要に応じて自動的に、別のスレッドで実行します。
 このメソッドを呼び出すには、System.gc() メソッドが一般的で便利です。

public long totalMemory()  //Runtime クラス ← java.lang パッケージ
 Java 仮想マシンのメモリの総容量を返します。
 ホストの環境によっては、このメソッドによって返される値が時間とともにに変化する場合があります。
 任意の指定された型のオブジェクトを格納するのに必要なメモリ容量は、実装によって異なります。
戻り値: 現在および将来のオブジェクトに利用可能な現在のメモリの総容量 (バイト単位)

public static Runtime getRuntime()  //Runtime クラス ← java.lang パッケージ
 現在の Java アプリケーションに関連した Runtime オブジェクトを返します。
 Runtime クラスのメソッドのほとんどはインスタンスメソッドであり、
 現在のランタイムオブジェクトに対応して呼び出されなければなりません。
戻り値: 現在の Java アプリケーションに関連した Runtime オブジェクト

"Hello!" と this と null

"Hello!" は「String クラスのインスタンス」と言いますが、
より正確には「String クラスのインスタンスを指すポインタ」です。
同様に this は「現在のインスタンス」ではなく「現在のインスタンスを指すポインタ」です。
また、null は「何もインスタンスを参照していない」ことを表す値です。

C++言語では「ポインタ->メンバー関数」でしたが、
Java言語では「ポインタ.メソッド」なのに注意!

例題15-2 配列を確保し続けて、ガーベッジコレクションを発生させる。

○プログラム

public class GcArray1{
  public static void main(String[] args){
    while(true){
      int[] a=new int[1000];
      System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());
    }
  }
}


○実行結果

……
残りメモリ=1432368
残りメモリ=1428160
残りメモリ=1423952
残りメモリ=1419744
残りメモリ=1415536
残りメモリ=1933352
残りメモリ=1929144
残りメモリ=1924936
残りメモリ=1920728
残りメモリ=1916520
……

○解説

ヒープ上のメモリは、クラスのインスタンスだけでなく、配列を確保する場合でも消費されます。
実際、Java言語の配列は java.lang.Object のインスタンスのように扱われるのです。
つまり、配列として確保したメモリも、誰からも参照されなくなるとガーベッジコレクションの対象になります。

例題15-3 ガーベッジコレクションが発生してもメモリが回収されないプログラム。

○プログラム

import java.util.*;

public class GcArray2{
  static Vector vector=new Vector();
  public static void main(String[] args){
    while(true){
      int[] a=new int[1000];
      vector.addElement(a);
      System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());
    }
  }
}


○実行結果

……
残りメモリ=45520
残りメモリ=41312
残りメモリ=37104
残りメモリ=32896
残りメモリ=28688
残りメモリ=24480
残りメモリ=20272
残りメモリ=16064
残りメモリ=11856
残りメモリ=7648
Exception in thread "main" java.lang.OutOfMemoryError
-- Press any key to exit (Input "c" to continue) --

○解説

public class Vector extends AbstractList
implements List, RandomAccess, Cloneable, Serializable
  //java.util パッケージ
 Vector クラスは、オブジェクトの可変長配列を実装します。
 ここには配列と同じように、整数インデックスを使ってアクセスできる要素が格納されています。
 しかし、Vector のサイズは、作成後に追加および削除されたオブジェクトを格納できるように
 必要に応じて増やしたり減らしたりすることができます。

public Vector()  //Vector クラス ← java.util パッケージ
 空の Vector を作成し、その内部データ配列のサイズが 10 で、
 その標準的な増分がゼロであるようにします。

public void addElement(Object obj)  //Vector クラス ← java.util パッケージ
 指定の要素を Vector の最後に追加し、サイズを 1 増やします。
 サイズが Vector の容量より大きくなるときは容量が増やされます。
パラメータ: obj - 追加される要素

8. vector.addElement(a);

確保した配列を指すポインタは可変長配列 Vector クラスの変数 vector に追加格納されるので、
いつまでたっても、vector から参照され続けることになります。
したがって、確保した配列がガーベッジになることはありません。

本当にメモリが不足すると、Java仮想マシンはエラー OutOfMemoryError をスローします。
実行結果でも OutOfMemoryError がスローされていますね。

言うまでもありませんが、このようなプログラムは危険です。
使わなくなったインスタンスを指すポインタは vector から抜き取って、
誰からも参照されないようにしましょう。

例題15-4 強制的にガーベッジコレクションを発生させる。

○プログラム

public class GcArray3{
  public static void main(String[] args){
    for(int n=0;true;n++){
      int[] a=new int[1000];
      if(n%100==0){
        System.out.println("gcを呼び出します。");
        System.gc();
      }
      System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());
    }
  }
}


○実行結果

……
残りメモリ=1534648
残りメモリ=1530440
残りメモリ=1526232
残りメモリ=1522024
残りメモリ=1517816
gcを呼び出します。
残りメモリ=1929976
残りメモリ=1925768
残りメモリ=1921560
残りメモリ=1917352
残りメモリ=1913144
……

○解説

ガーベッジコレクションが始まると、短い間ではありますが、プログラム全体が停止します。
それが困るというのであれば、自分で任意のタイミングでガーベッジコレクションを発生させましょう。

7. System.gc();

gc メソッドでガーベッジコレクションを発生させることが出来ます。

System クラスのメソッド

public final class System extends Object  //java.lang パッケージ
 System クラスには有用なクラスフィールドおよびメソッドがあります。
 インスタンスを生成することはできません。
 System クラスによって得られる機能には、標準入力、標準出力、およびエラー出力ストリーム、
 外部的に定義された「プロパティ」へのアクセス、ファイルおよびライブラリのローディング方法、
 配列の一部をすばやくコピーするユーティリティメソッドがあります。

public static void gc()  //System クラス ← java.lang パッケージ
 ガベージコレクタを実行します。
 gc メソッドの呼び出しは、現在占有しているメモリを再度迅速に利用するために、
 Java 仮想マシンが使用しないオブジェクトのリサイクルを実行することを意味します。
 メソッドの呼び出しから制御が戻るのは、Java 仮想マシンが、
 破棄されたオブジェクトが占有していたスペースを最大限まで回復し終えたときです。

finalize メソッド

finalize メソッドは、参照されなくなったインスタンスが
ガーベッジコレクションによって回収されるに呼び出されるメソッドです。
呼び出されるのは「ガーベッジになった瞬間」ではなく、
「ガーベッジコレクションが発生したとき」であることに注意して下さい。
したがって、ここにはインスタンスの終了処理を書きます。

public class Object  //java.lang パッケージ
 Object クラスは、クラス階層のルートです。
 すべてのクラスは、スーパークラスとして Object を持ちます。
 配列を含むすべてのオブジェクトは、このクラスのメソッドを実装します。

protected void finalize() throws Throwable  //Object クラス ← java.lang パッケージ
 このオブジェクトへの参照はもうないとガベージコレクションによって判断されたときに、
 ガベージコレクタによって呼び出されます。
 サブクラスは finalize メソッドをオーバーライドして、
 システムリソースを破棄したり、その他のクリーンアップを行うことができます。
 Object クラスの finalize メソッドは、特別な処理を行いません。
 通常は、何もしないで復帰します。
 Object のサブクラスは、この定義をオーバーライドすることができます。
 任意のオブジェクトについて Java 仮想マシンが finalize メソッドを複数回呼び出すことはありません。
 finalize メソッドによって例外がスローされると、finalize メソッドの処理は停止されます。
 そうでない場合は無視されます。
例外: Throwable - このメソッドで生じた Exception


戻る / ホーム