最近、Javaオブジェクトによって占有されているメモリを解放することを検討していました。その間、Javaでオブジェクトをコピーする方法(浅い/深い)と、使用中のオブジェクトを誤って消去/無効化するのを避ける方法について混乱しました。
次のシナリオを検討してください。
ArrayList<Object>
_をメソッドの引数として渡します。ArrayList<Object>
_をスレッドによって処理される実行可能クラスに渡す。ArrayList<Object>
_をHashMap
に入れます。これらの場合、_list = null;
_またはlist.clear();
を呼び出すと、オブジェクトはどうなりますか?どの場合にオブジェクトが失われ、どの場合に参照のみがnullに設定されますか?
私はそれがオブジェクトの浅いコピーと深いコピーに関係していると思いますが、どの場合に浅いコピーが起こり、どの場合に深いコピーがJavaで起こりますか?
まず、objectをnullに設定することはありません。その概念には意味がありません。 null
の値を変数に割り当てることができますが、「変数」と「オブジェクト」の概念を非常に慎重に区別する必要があります。すると、あなたの質問は一種の答えになります。
現在、「浅いコピー」対「深いコピー」の観点から-通常、浅いコピーには新しいオブジェクトの作成が含まれますが、既存のオブジェクトのフィールドを直接コピーするため、ここでは「浅いコピー」という用語を避ける価値があります。ディープコピーは、それらのフィールドによって参照されるオブジェクトのコピーも取得します(参照型フィールドの場合)。このような単純な割り当て:
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<String> list2 = list1;
...はどちらかその意味で浅いコピーまたは深いコピーを行いません。参照をコピーするだけです。上記のコードの後、list1
およびlist2
は独立変数です-それらはたまたま同じ値(参照)を持っています。一方の値を変更しても、もう一方には影響しません。
list1 = null;
System.out.println(list2.size()); // Just prints 0
variablesを変更する代わりに、変数の値が参照するオブジェクトに変更を加えると、その変更は他の変数からも見えるようになります。
list2.add("Foo");
System.out.println(list1.get(0)); // Prints Foo
元の質問に戻ります。マップ、リスト、配列などに実際のオブジェクトを保存することはありません。referencesのみを保存します。オブジェクトがガベージコレクションされるのは、「ライブ」コードがそのオブジェクトに到達する方法がなくなった場合のみです。したがって、この場合:
List<String> list = new ArrayList<String>();
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("Foo", list);
list = null;
... ArrayList
にはそれを参照するエントリがあるため、Map
オブジェクトはガベージコレクションできません。
変数をクリアするには
私の知識によると、
変数を再利用する場合は、使用します
Object.clear();
再利用しない場合は、定義します
Object=null;
注:removeAll()と比較して、clear()は高速です。
私が間違っている場合は、私を修正してください....
これは、各オブジェクトを参照している変数の数に依存します。これを説明するには、いくつかのコードが良いでしょう。
Object myAwesomeObject = new Object();
List<Object> myList = new ArrayList<Object>();
myList.add(myAwesomeObject);
myList = null; // Your object hasn't been claimed by the GC just yet, your variable "myAwesomeObject" is still refering to it
myAwesomeObject = null; // done, now your object is eligible for garbage collection.
したがって、ArrayListをメソッドなどへの引数として渡すかどうかには依存せず、オブジェクトをまだ参照している変数の数に依存します。
Java GCは、参照されていないオブジェクトを自動的に要求します。そのため、ほとんどの場合、参照をnull
として明示的に設定する必要があります
変数のスコープが終了するとすぐに、オブジェクトはGCの対象となり、他の参照がオブジェクトを指していない場合は解放されます。
Javaは値渡しですので、メソッドでリストをnull
として設定した場合、メソッドで渡された元の参照には影響しません。
public class A{
private List<Integer> list = new ArrayList<Integer>();
public static void main(String[] args) {
A a = new A();
B b = new B();
b.method(a.list);
System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException
}
}
class B{
public void method(List<Integer> list){
list = null;
//just this reference is set to null and not the original one
//so list of A will not be GCed
}
}
ArrayListをメソッドに渡した場合、たとえば呼び出し元のコードのどこかにリストへのライブ参照がある場合、list = nullは効果がありません。コードのどこかでlist.clear()を呼び出すと、このリストからのオブジェクトへの参照はnullになります。メソッドへの参照の受け渡しは、値による参照の受け渡しである浅いコピーではありません
リストをハッシュマップに入れると、ハッシュマップはリストへの参照を保持するようになります。
メソッドへの引数としてリストを渡すと、メソッドはメソッドの期間中、リストへの参照を持ちます。
操作するスレッドに渡すと、スレッドは終了するまでオブジェクトへの参照を持ちます。
これらのすべてのケースで、list = null
、参照は維持されますが、これらの参照が消えると消えます。
リストを単純にクリアすると、参照は有効のままになりますが、プログラマーに知られていない可能性があり、特にスレッドを使用している場合はバグと見なされる可能性があるため、突然空になったリストを指すようになります。
最近、Javaオブジェクトによって占有されているメモリを解放することを検討していました。
アドバイス。
通常、これについて考えるのは悪い考えです。そして、通常、「助け」をしようとするのは悪い考えです。 99.8%のケースでは、Javaガベージコレクターは、実際にそのままにしておけば、ガベージコレクションのより良い仕事をすることができます...そしてあなたの努力を無駄にしないでください。 null
を物事に割り当てることで、実際に、nullしているフィールドが到達不可能なオブジェクトにある可能性があります。その場合、GCはフィールドを参照しません。無効にしました。
この(実用的な)ビューを採用する場合、浅いコピーと深いコピー、およびnullを使用しても安全な場合についてのあなたの考えはすべて意味がありません。
中程度または長期のストレージリークを回避するためにnull
...を割り当てることをお勧めするケースはごくわずかです。そして、オブジェクトを「リサイクル」しているというまれな状況のいずれかに実際にある場合は、ヌルを使用することをお勧めします。