<本章の目的>メモリ解放の仕組みを理解する。
●例題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 collection )というのは、
誰からも参照されていないインスタンス(すなわちガーベッジ)が占めているメモリ領域を回収し、
再利用することです。
ガーベッジコレクションを行う機構のことをガーベッジコレクタ( garbage collector )と呼びます。
コレクション( collection )は集めること、コレクタ( collector )は集める人のことです。
String s=new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
s は String 型の変数です。
インスタンスではありません。
インスタンスはプログラムでは表現されないけれど、メモリのどこかにある実体です。
そして、それを指すポインタが参照型の変数になります。
変数とインスタンスは混同しやすいですが、違いの分かるプログラマになりましょう(笑)!
System.out.println("残りメモリ="+Runtime.getRuntime().freeMemory());
Runtime.getRuntime() は、現在のランタイム(動作環境)を表すインスタンスを得るクラスメソッドです。
freeMemory() は、空きメモリ量を得るメソッドです。
さて、プログラムは無駄にゴミを生成していき、
残りメモリはだんだんと減りますが、あるとき突然残りメモリが増えています。
このとき、ガーベッジコレクションが発生しているのですね。
○補足
インスタンスが確保されているメモリ領域のことをヒープ( heap )といいます。
○補足2
String s=new String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
は、次のように書いても同じです。
String s="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public String(String original)
新しく生成された String オブジェクトを初期化して、引数と同じ文字シーケンスを表すようにします。
つまり、新しく作成された文字列は引数文字列のコピーになります。
★ Runtime クラスのメソッド
public long freeMemory()
Java 仮想マシン内の空きメモリの量を返します。
gc メソッドを呼び出すと、freeMemory によって返される値が増える場合があります。
戻り値: 将来割り当てられるオブジェクトに利用可能な現在のメモリの総容量 (バイト単位)
public long totalMemory()
Java 仮想マシンのメモリの総容量を返します。
ホストの環境によっては、このメソッドによって返される値が時間とともにに変化する場合があります。
任意の指定された型のオブジェクトを格納するのに必要なメモリ容量は、実装によって異なります。
戻り値: 現在および将来のオブジェクトに利用可能な現在のメモリの総容量 (バイト単位)
public void gc()
ガベージコレクタを実行します。
このメソッドを呼び出すと、Java 仮想マシンは使用していないオブジェクトをリサイクルし、
使用中のメモリをすばやく再利用可能な状態にします。
メソッド呼び出しから制御が戻された時点で、
仮想マシンは破棄されたオブジェクトをすべて再利用するよう最善を尽くしたことになります。
gc というメソッド名は「garbage collector」の頭字をとったものです。
gc が明示的には呼び出されなかった場合でも、
仮想マシンはこの再利用プロセスを必要に応じて自動的に、別のスレッドで実行します。
このメソッドを呼び出すには、System.gc() メソッドが一般的で便利です。
public static Runtime getRuntime()
現在の 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) --
○解説
Vector クラスは、オブジェクトの可変長配列を実装します。
public void addElement(Object obj)
指定の要素を Vector の最後に追加し、サイズを 1 増やします。
vector.addElement(a);
確保した配列へのポインタは
可変長配列 Vector 型のクラスフィールド vector に追加格納されるので、
いつまでたっても、vector から参照され続けることになります。
したがって、確保した配列がガーベッジになることはありません。
本当にメモリが不足すると、Java仮想マシンはエラー 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
……
○解説
ガーベッジコレクションが始まると、短い間ではありますが、プログラム全体が停止します。
それが困るならば、自分で発生させましょう。
System.gc();
gc メソッドでガーベッジコレクションを発生させることが出来ます。
★ System クラスのメソッド
public static void gc()
ガベージコレクタを実行します。
gc メソッドの呼び出しは、現在占有しているメモリを再度迅速に利用するために、
Java 仮想マシンが使用しないオブジェクトのリサイクルを実行することを意味します。
メソッドの呼び出しから制御が戻るのは、Java 仮想マシンが、
破棄されたオブジェクトが占有していたスペースを最大限まで回復し終えたときです。
System.gc() の呼び出しの動作は、実際には次の呼び出しと同じです。
Runtime.getRuntime().gc()
★ finalize
finalize メソッドは、参照されなくなったインスタンスが
ガーベッジコレクションによって回収される前に呼び出されるメソッドです。
呼び出されるのは、ガーベッジになった瞬間ではなく、
ガーベッジコレクションが発生したとき、であることに注意して下さい。
したがって、ここにはインスタンスの終了処理を書きます。
Object クラスで以下のように定義されています。
protected void finalize() throws Throwable
このオブジェクトへの参照はもうないとガベージコレクションによって判断されたときに、
ガベージコレクタによって呼び出されます。
サブクラスは finalize メソッドをオーバーライドして、システムリソースを破棄したり、
その他のクリーンアップを行うことができます。
Object クラスの finalize メソッドは、特別な処理を行いません。
通常は、何もしないで復帰します。
Object のサブクラスは、この定義をオーバーライドすることができます。