- PR -

メモリの使用・解放について

1
投稿者投稿内容
未記入
会議室デビュー日: 2006/06/18
投稿数: 5
投稿日時: 2006-06-21 23:01
コードの書き方でメモリの使用効率に違いが出ると聞いたのですが、
staticな属性を利用した場合のメモリの使用・解放のタイミングがよくわかりません。
認識はあっているでしょうか?

1."aaa"はGCの対象になる。
コード:

String val1 = "aaa";
val2 ="bbb";



2.method1実行時、param1が"case1"のとき、
 Testクラスの"case1"が生成される。"case2","val2"は生成されない。
コード:

Test test = new Test();
test.method1("case1");

class Test{
public String method1 (String param){
if( "case1".equals( param1 ) ){
result = "val1";
}else if( "case2".equals( param1 ) ){
result = "val2";
}
}
}



3.test1のインスタンス生成時に"case1","case2","val1","val2"は生成される。
 test2のときは生成されない。
 "case1","case2","val1","val2"はGCの対象でない。
コード:

Test test1 = new Test();
test1.method1("case1");

Test test2 = new Test();
test2.method1("case1");

class Test{
private static final String CASE1 = "case1";
private static final String CASE2 = "case2";
private static final String VALUE1 = "val1";
private static final String VALUE2 = "val2";

public String method1 (String param){
String result = null;
if( CASE1.equals( param ) ){
result = VALUE1;
}else if( CASE2.equals( param ) ){
result = VALUE2;
}
return result;
}
}



4.test生成時に"key1","val1","key2","val2"が生成される。
 mapおよび"key1","val1","key2","val2"はGCの対象でない。
コード:

Test test1 = new Test();
test1.method1("key1");
Test test2 = new Test();
test2.method1("key1");

class Test{
private static HashMap map = null;

public Test(){
if(map == null){
map = new HashMap;
map.put("key1","val1");
map.put("key2","val2");
}
}
public String method1(String key){
return map.get(key);
}
}




[ メッセージ編集済み 編集者: 未記入 編集日時 2006-06-21 23:02 ]
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-06-22 00:00
引用:

未記入さんの書き込み (2006-06-21 23:01) より:
コードの書き方でメモリの使用効率に違いが出ると聞いたのですが、
staticな属性を利用した場合のメモリの使用・解放のタイミングがよくわかりません。
認識はあっているでしょうか?


文字列リテラルは定数プールと呼ばれる領域に確保されます。
インスタンスの生成時に一緒に生成されるわけではないです。

staticに代入しなくとも文字列リテラルは定数プールにいます。

#定数プール中のオブジェクトは回収されないかもしれません。
#仕様を調べたことないので不明ですが、それでもよいのかも?

引用:

1."aaa"はGCの対象になる。


なりません。

"aaa"が、new String("aaa")したものなら、
明示的にnewしたインスタンスは対象になります。

引用:

2.method1実行時、param1が"case1"のとき、
 Testクラスの"case1"が生成される。"case2","val2"は生成されない。


staticなフィールドが初期化されるタイミングはクラスのロード時です。
初回の参照時ではありません。クラスがロードされるタイミングは、
(リフレクションを使う場合を除いて)最初に使用された時です。

逆に、クラスをアンロードできるタイミングとは、
そのクラスをロードしたクラスローダ全体に到達できなくなった時です。
クラスローダは自分がロードしたクラスを参照しているので、自分自身と
ロードした全てのクラスが孤立した時に解放されることになります。

引用:

3.test1のインスタンス生成時に"case1","case2","val1","val2"は生成される。
 test2のときは生成されない。
 "case1","case2","val1","val2"はGCの対象でない。


Testクラスがロードされていない場合はtest1の生成直前に初期化されますが、
インスタンスの生成時というと少し語弊があると思います。

(少なくとも)クラスがアンロードされるまではGC対象にはなりません。

引用:

4.test生成時に"key1","val1","key2","val2"が生成される。
 mapおよび"key1","val1","key2","val2"はGCの対象でない。


生成されたHashMapはstaticなのでアンロードまではGC対象になりません。
到達可能なHashMapのインスタンスから参照されたオブジェクトもGC対象に
なりませんし、文字列リテラルは元々定数プールから参照されてます。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2006-06-22 00:05
まず最初に、リテラル文字列(ソースコード中に即値で記述された文字列)は一般のオブジェクトとは異なる扱いを受けることに留意して下さい。

・リテラル文字列をインスタンス化しようとした時点で文字列プールを検索して同じ文字並びのインスタンスが既に存在した場合は、それが使い回しされる。

・存在しなかった場合は新たなStringオブジェクトが生成される。それは、将来の使い回しにそなえて文字列プールに登録される。

・リテラル文字列から生成されたStringオブジェクトは文字列プールに登録されているので、決してGCされない。

1. NO
リテラル文字列から生成されたStringオブジェクトは決してGCされない。

2.
「"val1"が生成される。"case2","val2"は生成されない」の書き間違いなら、たぶんYES。
ただし、Test.classをロードした時点で、その中に含まれるリテラル文字列をすべてintern()してしまうような実装もあるかも。

3. NO
Test.classをロードした直後に、static initilizerによって "case1","case2","val1","val2"が生成される。それは、test1のインスタンス生成よりも前である。
GCの対象でないという点に関しては、YES。

4. YES
ただし、mapがGCされないのと、リテラル文字列がGCされないのは、理由が異なる。
Test.classをロードしたClassLoaderが破棄されるとmapはGCされるが、その場合でもリテラル文字列はGCされない。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2006-06-22 01:09
なんか微妙に回答が先行したようなので解説を補足します。

リテラル文字列の文字並びはクラスのコンスタントプールに存在していますが、そこから実際にStringオブジェクトが生成されるのは、オブジェクト参照が必要になる実行時です。

例3のようにstaticメンバーに代入していると、classロード時にオブジェクトが必要になるので、その時点でインスタンスが生成されます。

例2のようにリテラル文字列が実行文に埋め込まれている場合は、メッソド呼び出しによって該当部分が実行されるまでは、インスタンスが生成されません。

リテラル文字列から生成されたStringオブジェクトは、Stringクラスの管理下にある文字列プールに登録されるので、それがどのクラスに由来するかは区別されません。元になったクラスがアンロードされた場合でも、そのクラスのリテラル文字列から生成されたStringオブジェクトは解放されません。
未記入
会議室デビュー日: 2006/06/18
投稿数: 5
投稿日時: 2006-06-23 02:07
コメントありがとうございます。
大変勉強になりました。

引用:

リテラル文字列の文字並びはクラスのコンスタントプールに存在していますが、そこから実際にStringオブジェクトが生成されるのは、オブジェクト参照が必要になる実行時です。



2.と3.でどちらがメモリの効率がいいのだろうという疑問が発端だったのですが、
リテラル文字列がプールされているのなら、文字列が利用するメモリには差がないのですね。
3.の方がロード時に必要なメモリが大きく、ロードの時間も比較的かかるのでしょうね。
1

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