- PR -

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

投稿者投稿内容
begood
ベテラン
会議室デビュー日: 2003/09/12
投稿数: 97
お住まい・勤務地: とうきょー
投稿日時: 2003-11-17 19:03
String型は、参照していくのではなく、新たに領域を作成して同じ値とか、ものがはいるのではなかったでしたっけ?(記憶がうすく・・・、わすれかけ・・・また、こんな紛らわしいコードを開発中に書いていたら、周りから、とばっちりがきますね、きっと)となると、問題文の"ABC"の入っている領域は変数aで、b[0]には問題文では聞いていない同じ値でメモリの領域も異なる"ABC"が入っているのでは・・・。メモリの領域も実際の値も違うもので・・・。問題文からすると、聞いているのは"ABC"の入っているとしか記載されていないので、変数aがGCされることを聞いているのかと思いました。

Object型で確認をするのはちょっと難しいのでは・・・?。nullをいれての確認で、単位、System.gc()をいれても、デバッグはできないと思われます。また、変数の中の値をみても、この問題の確認をすることはできないと思います。GCはかなり高度にできています。だから、JAVAはCよりも、実際の開発中にバグが少なく、メモリにこだわりを持たずに楽に開発ができると思い・・・。問題文に"ABC"と記載するのではなく、変数aの内容がGCされるタイミングなんて記載したら、誰でも回答できる問題になってしまい・・・。いずれにせよ、開発をする際にはあまり関係のないところのように思われ。

私の勘違いであれば、申し訳なく、すいません。(^^;


moke
会議室デビュー日: 2003/09/22
投稿数: 6
投稿日時: 2003-11-17 20:15
ずっと何がおかしいのか悩んでいました。

a="ABC"としてaに"ABC"のポインタが格納し、
b[0]=a;とするとb[0]に「aのポインタ」が格納されるのではなく、
「aに格納されているポインタと同じメモリ領域を指す別のポインタ」が格納される。

よってaとbに格納されている物は同一の物ではなく、
a=null;としてもb[0]にはポインタが依然存在するので"ABC"はGCの対象にならず、
b[0]=null;とした時点で参照が無くなる為"ABC"はGCの対象になる。

というふうな理解でよろしいのでしょうか?

(追記)
「メモリ領域」は「オブジェクト」、
「ポインタ」は「(オブジェクトへの)参照」と読み替えてくださいm(_ _)m

[ メッセージ編集済み 編集者: moke 編集日時 2003-11-17 21:17 ]
たーぞう
ぬし
会議室デビュー日: 2003/08/08
投稿数: 317
お住まい・勤務地: お花畑
投稿日時: 2003-11-17 20:57
僕もよくわからないんですけど、もしかして

b[0] = a;

とやった時に、"ABC"というオブジェクトが新たに生成されて、b[0]には新しいオブジェクトへの参照が格納される・・・なんてことが起きていたとすれば現象面の説明はできますね。

ただし責任は持てません^^;
アティ
ベテラン
会議室デビュー日: 2003/08/14
投稿数: 91
お住まい・勤務地: KANAGAWA
投稿日時: 2003-11-17 21:42
引用:

String型は、参照していくのではなく、新たに領域を作成して同じ値とか、ものがはいるのではなかったでしたっけ?(記憶がうすく・・・、わすれかけ・・・また、こんな紛らわしいコードを開発中に書いていたら、周りから、とばっちりがきますね、きっと)となると、問題文の"ABC"の入っている領域は変数aで、b[0]には問題文では聞いていない同じ値でメモリの領域も異なる"ABC"が入っているのでは・・・。メモリの領域も実際の値も違うもので・・・。問題文からすると、聞いているのは"ABC"の入っているとしか記載されていないので、変数aがGCされることを聞いているのかと思いました。



String型だろうがObject型だろうがどちらも同じ動きをしますよ。
コード:
1:public class Sample {
2:	public static void main(String args[]){
3:		String[] b = new String[1];
4:		String a = "ABC";
5:		b[0] = a;
6:		a = null;
7:		b[0] = null;
8:		b = null;
9:		System.out.println("Bye");		
10:	}
11:}


3行目でString配列型のインスタンスが作成されて、それの参照がbに入ります。
コード:
            ┏━━━┓
            ┃┏━┓┃
            ┃┃  ┃┃
            ┃┗━┛┃
            ┃  0   ┃
            ┗━━━┛
ヒープ領域      ↑
               │
┏━━━━━━┳┿┳━┓
┃スタック領域┃・┃  ┃
┗━━━━━━┻━┻━┛
                b


次に4行目でString型のインスタンス("ABC")が作成されて、それの参照がaに入ります。
コード:
            ┏━━━┓
            ┃┏━┓┃  ┏━━┓
            ┃┃  ┃┃  ┃ABC ┃
            ┃┗━┛┃  ┗━━┛   
            ┃  0   ┃    ↑
            ┗━━━┛    │
ヒープ領域      ↑  ┌──┘
               │  │
┏━━━━━━┳┿┳┿┓
┃スタック領域┃・┃・┃
┗━━━━━━┻━┻━┛
                b   a


そして、5行目でb[0]がaのインスタンスを指すようになります。
コード:
            ┏━━━┓
            ┃┏━┓┃  ┏━━┓
            ┃┃・╂╂→┃ABC ┃
            ┃┗━┛┃  ┗━━┛   
            ┃  0   ┃    ↑
            ┗━━━┛    │
ヒープ領域      ↑  ┌──┘
               │  │
┏━━━━━━┳┿┳┿┓
┃スタック領域┃・┃・┃
┗━━━━━━┻━┻━┛
                b   a


この後6行目でa=nullとすると、変数aからString型インスタンス"ABC"は参照されなくなります。
コード:
            ┏━━━┓
            ┃┏━┓┃  ┏━━┓
            ┃┃・╂╂→┃ABC ┃
            ┃┗━┛┃  ┗━━┛   
            ┃  0   ┃
            ┗━━━┛
ヒープ領域      ↑
               │
┏━━━━━━┳┿┳━┓
┃スタック領域┃・┃  ┃
┗━━━━━━┻━┻━┛
                b   a


7行目でb[0]からString型インスタンス"ABC"が参照されなくなります。
コード:
            ┏━━━┓
            ┃┏━┓┃  ┏━━┓
            ┃┃  ┃┃  ┃ABC ┃
            ┃┗━┛┃  ┗━━┛   
            ┃  0   ┃
            ┗━━━┛
ヒープ領域      ↑
               │
┏━━━━━━┳┿┳━┓
┃スタック領域┃・┃  ┃
┗━━━━━━┻━┻━┛
                b   a


最後に8行目でbからString配列型インスタンスが参照されなくなります。
コード:
            ┏━━━┓
            ┃┏━┓┃  ┏━━┓
            ┃┃  ┃┃  ┃ABC ┃
            ┃┗━┛┃  ┗━━┛   
            ┃  0   ┃
            ┗━━━┛
ヒープ領域      
                
┏━━━━━━┳━┳━┓
┃スタック領域┃  ┃  ┃
┗━━━━━━┻━┻━┛
                b   a


ここで問題となるのがガーベッジコレクションの対象となる条件ですが、
これは、スタック領域からたどれなくなったインスタンスが対象になります。
なので、6行目の時点では、aからはたどることは出来ませんが、bからたどることが出来るので、
ガーベッジコレクションの対象にはなりません。
次の7行目の時点でインスタンス"ABC"をスタック領域からたどれなくなるので、
ガーベッジコレクションの対象となります。
で、皆こう思っているので、2番目の問題が腑に落ちないと言っているんですよ。

引用:

Object型で確認をするのはちょっと難しいのでは・・・?。nullをいれての確認で、単位、System.gc()をいれても、デバッグはできないと思われます。また、変数の中の値をみても、この問題の確認をすることはできないと思います。GCはかなり高度にできています。だから、JAVAはCよりも、実際の開発中にバグが少なく、メモリにこだわりを持たずに楽に開発ができると思い・・・。問題文に"ABC"と記載するのではなく、変数aの内容がGCされるタイミングなんて記載したら、誰でも回答できる問題になってしまい・・・。いずれにせよ、開発をする際にはあまり関係のないところのように思われ。


CよりはJavaはメモリ管理の煩わしさはありませんが、
ぜんぜん考えないでいいということじゃないです。
ガーベッジコレクションの機能上スタック領域から辿れてしまうと、
生きているインスタンスだと判断されて、ずっとメモリ上に居座りつづけます。
こうなるとどうなるかは、参考文献を参照してください。
終(ふ〜)

参考文献:Java PRESS Vol.32 92ページ(今書店に並んでいるやつです。)
begood
ベテラン
会議室デビュー日: 2003/09/12
投稿数: 97
お住まい・勤務地: とうきょー
投稿日時: 2003-11-18 03:20
日本語での説明に限界を感じ・・・。
配列と参照と、ガーベッジコレクションと、そのほかにもいろいろとありますけど、説明にちと、ギブアップといった感じです。でも、これだけ、全員が同じ意見であれば、私の考えが間違っているのは間違いないでしょう。

試験対策の時にJAVAのソースコードをひっくり返して見返したときに何かを私は勘違いしてしまっ他のだと思います。それにコード内のGCは私レベルでは難しすぎて・・・。普通に考えている内容ととってもかけ離れているものだったので。

でも、下の図、なんか、配列を使用した参照の場合だと、おかしいような気がするのはわたしだけですかねぇ・・・。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2003-11-18 11:15
おそらく、出題者の意図としてはアティさんの説明のようなことなのでしょうが…

実際は、文字列リテラルはすべて文字列プールに格納され、ガベコレされることはありません。
(String#intern()の説明を参照)

問題1で、new String("Hello") としてわざわざ新たなStringオブジェクトを生成しているのは、
この制約から逃れるためでしょう。
問題2は、「出題者の間違い」というのが正解ではないかと。
begood
ベテラン
会議室デビュー日: 2003/09/12
投稿数: 97
お住まい・勤務地: とうきょー
投稿日時: 2003-11-19 11:25
前に確認したときは教科書と実際の動きが違っていたので、なんとなく、確認してみたくなり、ちょっと、メモリを確認してみました。回答はb=nullの後ですね。みんな不正解のようです・・・。まだ、はっきりと断言できず、確認することがほかにもあるので、もう少し確認しようかと。ただ、コンパイラのバージョンにもよる感じがして、前に確認したときとも結果が異なっているような気がしますが、コンパイラーのバージョンを覚えておらず・・・。参考書には一律、皆さんが説明させた形で記載しているような感じがします。GCに関してはドキュメントにも明確に記載していることがなく、Sunがもしかしたら、後に変更するかもしれないので、このような形にしているのかもしれません。なので、もう少し、調べる必要があります。ただ、調べるにあたり、どなたか、変数の格納されているメモリアドレスをJAVAのメソッドで確認する方法をアドバイスもらえませんか?一応確認しているコードを乗せます。何か勘違いしている部分があったら教えていただきたく。(未熟者なのでお願いします)

Cを使用してNativeメソッドを使用してもいいのですが、面倒で・・・。
public class Sample {
public static void main(String args[]){
Runtime rt = Runtime.getRuntime();
long l1 = 0L;

String[] b = new String[1];
String a = "ABC";
b[0] = a;
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
l1 = rt.freeMemory();
System.out.println("l1 : " + l1);

a = null;
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
l1 = rt.freeMemory();
System.out.println("l2 : " + l1);

b[0] = null;
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
l1 = rt.freeMemory();
System.out.println("l3 : " + l1);


b = null;
System.gc();
System.gc();
System.gc();
System.gc();
System.gc();
l1 = rt.freeMemory();
System.out.println("l4 : " + l1);


System.out.println("Bye");
}
}


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

引用:

begoodさんの書き込み (2003-11-19 11:25) より:
String[] b = new String[1];
String a = "ABC";



String を使わないようにしてみました。ちょっと長いですがぜんぶ載せます。
コード:
public class Sample {
    
    private static class Foo {

        protected void finalize() throws Throwable {
            System.out.println("finalize: " + this);
            super.finalize();
        }
    }
    
    public static void main(String args[]) {
        Runtime rt = Runtime.getRuntime();
        long l1 = 0L;

//        String[] b = new String[1];
//        String a = "ABC";
        Object[] b = new Object[1];
        Object a = new Foo();
        b[0] = a;
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        l1 = rt.freeMemory();
        System.out.println("l1 : " + l1);

        a = null;
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        l1 = rt.freeMemory();
        System.out.println("l2 : " + l1);

        b[0] = null;
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        l1 = rt.freeMemory();
        System.out.println("l3 : " + l1);

        b = null;
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        System.gc();
        l1 = rt.freeMemory();
        System.out.println("l4 : " + l1);

        System.out.println("Bye");
    }
}



こうすると、結果は大別すると2通りあり、
コード:
l1 : 1938792
l2 : 1938704
finalize: Sample$Foo@15601ea
l3 : 1938744
l4 : 1938760
Bye


の場合と、
コード:
l1 : 1938664
l2 : 1938704
l3 : 1938528
finalize: Sample$Foo@15601ea
l4 : 1938760
Bye


の場合に分かれました。

System.out.println は内部で synchronized していますので、
表示は早い者勝ちだと思いますので、これから言えることは、
String クラスでなければ、
b[0] = null; と b = null; の間に finalize が動くこともありえる、
すなわち b = null; よりも早い時点でガーベッジコレクションが動くことがありえる、
と言えます。

もとの問題を厳密に捉えると対象は String クラスなので、
finalize を挟み込めませんので、分かりませんでした。
というか、Java においては String は特殊扱いのクラスなので、
初学者向けのサンプルや試験問題にはあまり取り上げないほうが良いのでは、
という気持ちを持っています。

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