- PR -

ガベージコレクションの対象となるタイミングについて

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-11-20 17:12
unibon です。こんにちわ。

引用:

unibonさんの書き込み (2003-11-19 23:23) より:
なお、その後、String の finalize をオーバライド(String で Object.finalize をオーバライド)するために、
「例外処理Exception につて」
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=6544&forum=12
のように rt.jar を書き換えてみましたが、
String クラスに finalize メソッドを追加したものを実行すると
native な部分で落ちてしまい、ダメでした。


あまりたいしたことではないのですが、
final なクラスなのに勝手にメソッド宣言に関する部分まで改造してしまうと
VM の中で finalize をオーバライドしていないことを前提にしている箇所と
矛盾してしまうためなのかな、と今のところ思っています。
#既存のメソッドの中にコードを追加する分には構わないけど。
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-11-20 18:23
引用:

びしばしさんの書き込み (2003-11-20 16:54) より:
Wataさんの例は、string というオブジェクトが gc されることを示すだけで、"Test" が gc されるかどうかは示していないと思います。


ローカル変数stringで参照されいたのは"Test"のリテラルが示すStringオブジェクトであることは間違いないと思いますが...。
ちなみに、mainメソッド内などで"Test"というリテラルを使っていた場合や、class1, loader, objectのいずれかにnullを代入しなかった場合などはstringRefの参照先オブジェクトは解放されません。
Keisuke
大ベテラン
会議室デビュー日: 2003/10/24
投稿数: 105
投稿日時: 2003-11-20 23:39
確かにWataさんの、
「クラスがアンロードされるまでだと思います。」が正しそうです。
ただ、「同一の文字リテラルを持つ全ての」を頭に加えたほうがいいかな。
H2
ぬし
会議室デビュー日: 2001/09/06
投稿数: 586
お住まい・勤務地: 港
投稿日時: 2003-11-21 07:44
引用:

びしばしさんの書き込み (2003-11-20 16:54) より:
Wataさんの例は、string というオブジェクトが gc されることを示すだけで、"Test" が gc されるかどうかは示していないと思います。


私もそう思います。java.lang.String#internのドキュメントに書いてあるように、文字プールはStringクラスが持っているもののはず。UnLoadedClassがアンロードされたからといって、文字プールの"Test"が消されたわけではありません。

Wataさんの例の string = null; だけをコメントアウトして、java -verbose:gc ClassUnloadTest で実行してみると、
コード:
[Full GC[Unloading class UnLoadedClass]
 158K->94K(1984K), 0.0215695 secs]
GC executed!
ClassLoader : null
Class : null
Object : null
String : Test


という結果が出ます。これを見ると、UnLoadedClassがアンロードされているのにもかかわらず、文字リテラルは存在しています。前にも言いましたが、internされたStringオブジェクトはStringクラスがアンロード(VMが終了)するまでGCの対象にならないと思います。(とは言ったものの、あまりにもプールのサイズが大きくなったらどうするんだろう?)

#私はガーベッジコレクションに一票。
H2
ぬし
会議室デビュー日: 2001/09/06
投稿数: 586
お住まい・勤務地: 港
投稿日時: 2003-11-21 09:07
とっても気になったのでもうちょっと深く調べました。
「internされている文字列はGCの対象になるのか」

internされている文字列とは、コンパイル時に作られる文字列(クラスのロード時にプールされる)と実行時に明示的にinternされる事をさします。

"A pool of strings, initially empty, is maintained privately by the class String." とJava APIドキュメントに書いてあるので文字列プールはStringクラスが持っていることは間違いありません。クラスがロードされると、コンパイル時に用意されている文字列がプールに追加されます(言語仕様 12.5)。言語仕様 3.10.5より、これらの文字列はinternされていないといけません。実行時にinternを呼び出すと、プールに新しい文字列を追加し、プールにある文字列への参照を返します。

さて、一旦プールされた文字列がGCされるかどうかですが、Java言語仕様にも、JVM仕様にも、APIにもプールされたStringオブジェクトがGCの対象にされないとはどこにも書かれていません。そのため、JVMとAPIの実装によってはプールされている文字列もGCの対象になりえます。

実行時にinternされた文字列は、誰からも参照されなくなった時点で他のオブジェクト同様にGCの対象になりえます。クラスのロード時に追加された文字列はクラスがアンロードされて、かつ誰からも参照されなくなった時点でGCの対象になりえます。

つまり、私が前に言った「internされたStringオブジェクトはStringクラスがアンロード(VMが終了)するまでGCの対象にならない」は誤っています。

以上、Java Technology Forums (http://forum.java.sun.com/) の過去ログ(string poolで検索)で調べてきました。

さてさて、また話を蒸し返しますが、最初に忍者鳥取県さんが聞いた問題においては、"ABC"がコンパイル時に作られる文字列であるため、a = null; とした時にGCの対象になることは絶対にありえません。また、クラスがアンロードされるのがmainを抜けた後なので、b[0]=null; や b=null; としても"ABC"はまだGCの対象にはなりえません。

もし、String a = "ABC"; が String a = new String(new char[]{'A', 'B', 'C'}); a = a.intern(); となっていれば、プールされている"ABC"は b[0]=null; とした時点でGCの対象になりえます。

コード:
//プログラムが終了するまで"ABC"が残るケース
public class Sample {
	public static void main(String args[]){
		String[] b = new String[1];
		String a = "ABC"; 
		b[0] = a;
		a = null;
		b[0] = null;
		b = null;
		System.out.println("Bye");
                //Class がアンロードされるまで"ABC"はGCの対象にならない。
	}
}

//プログラムが終了する前に"ABC"がGCの対象となりうるケース
public class Sample {
	public static void main(String args[]){
		String[] b = new String[1];
		//new char[]{'A', 'B', 'C'}とするのは"ABC"がコンパイル時に
		//プールされるのを防ぐため
		String a = new String(new char[]{'A', 'B', 'C'}); 
		a = a.intern(); //文字プールに"ABC"を追加
		b[0] = a;
		a = null;
		b[0] = null; 	//この時点で文字プールの"ABC"をだれも参照していないので、
				//"ABC"はGCの対象になりうる。
		b = null;
		System.out.println("Bye");
                //Class がアンロードされるまで"ABC"はGCの対象にならない。
	}
}

Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-11-21 09:43
引用:

Keisukeさんの書き込み (2003-11-20 23:39) より:
確かにWataさんの、
「クラスがアンロードされるまでだと思います。」が正しそうです。
ただ、「同一の文字リテラルを持つ全ての」を頭に加えたほうがいいかな。


すみません。そのとおり。省略してしまいました。
イメージ的には、Classオブジェクトが自クラス内で使用するリテラルのintern済みオブジェクトの参照を持っていると考えればよいのではないでしょうか?
そしてinternされたオブジェクトも、全ての参照がなくなればGC対象となる。(プール内のオブジェクトも例外的な扱いはされない。)

引用:

H2さんの書き込み (2003-11-21 07:44) より:
Wataさんの例の string = null; だけをコメントアウトして、java -verbose:gc ClassUnloadTest で実行してみると、


おお!-verbose:gcオプションをつければクラスがアンロードされたかどうかはすぐわかるのですね。ちなみに、string = null;をしない場合は自明だと思ったので、説明を省いてしまいました。

スキルアップ/キャリアアップ(JOB@IT)