null
Javaで未使用のオブジェクト参照を割り当てると、ガベージコレクションプロセスが測定可能な方法で改善されますか?
私のJava(およびC#)の経験から、仮想マシンやJITコンパイラーを試してみるのは直感に反することが多いことがわかりましたが、同僚がこの方法を使用しているのを見て、これが良い習慣かどうか知りたいです。アップまたはそれらのブードゥープログラミングの迷信の1つ?
null
Javaで未使用のオブジェクト参照を割り当てると、ガベージコレクションプロセスが測定可能な方法で改善されますか?
私のJava(およびC#)の経験から、仮想マシンやJITコンパイラーを試してみるのは直感に反することが多いことがわかりましたが、同僚がこの方法を使用しているのを見て、これが良い習慣かどうか知りたいです。アップまたはそれらのブードゥープログラミングの迷信の1つ?
回答:
通常、いいえ。
しかし、すべてのものと同様に、それは状況によって異なります。最近のJavaのGCは非常に優れており、到達できなくなった直後にすべてをクリーンアップする必要があります。これは、ローカル変数のメソッドを終了した直後であり、クラスインスタンスがフィールドに対して参照されなくなったときです。
それ以外の場合は参照されたままになることがわかっている場合にのみ、明示的にnullを指定する必要があります。たとえば、保持されている配列。配列の個々の要素が不要になったら、それらをnullにすることができます。
たとえば、ArrayListの次のコード:
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
また、オブジェクトを明示的にnullにすると、参照が残っていない限り、オブジェクトが自然にスコープから外れた場合よりも早くオブジェクトが収集されることはありません。
両方とも:
void foo() {
Object o = new Object();
/// do stuff with o
}
そして:
void foo() {
Object o = new Object();
/// do stuff with o
o = null;
}
機能的に同等です。
System.gc()
、削除されます(そのオブジェクトを指す他の参照がない場合)。
私の経験では、多くの場合、人々は必要性からではなく、妄想から参照を無効にします。簡単なガイドラインは次のとおりです。
オブジェクトAがオブジェクトBを参照していて、この参照が不要になり、オブジェクトAがガベージコレクションの対象にならない場合は、フィールドを明示的に無効にする必要があります。とにかく囲んでいるオブジェクトがガベージコレクションを取得している場合は、フィールドをnullにする必要はありません。dispose()メソッドでフィールドを無効にすることは、ほとんどの場合役に立ちません。
メソッドで作成されたオブジェクト参照を無効にする必要はありません。メソッドが終了すると、それらは自動的にクリアされます。このルールの例外は、非常に長いメソッドまたは大規模なループで実行していて、メソッドが終了する前に一部の参照がクリアされるようにする必要がある場合です。繰り返しますが、これらのケースは非常にまれです。
ほとんどの場合、参照を無効にする必要はありません。ガベージコレクターの裏をかくことは無意味です。あなたはただ非効率的で読めないコードになってしまうでしょう。
良い記事は、今日のコーディングの恐怖です。
GCの動作は、ポインタを持たないオブジェクトを検索することです。検索の領域は、ヒープ/スタックおよびその他のスペースです。したがって、変数をnullに設定すると、実際のオブジェクトは誰にもポイントされないため、GCされる可能性があります。
ただし、GCはその瞬間に実行されない可能性があるため、実際には何も購入していない可能性があります。ただし、メソッドが(実行時間の点で)かなり長い場合は、GCがそのオブジェクトを収集する可能性が高くなるため、それだけの価値がある可能性があります。
この問題はコードの最適化でも複雑になる可能性があります。変数をnullに設定した後で変数を使用しない場合は、値をnullに設定する行を削除するのが安全な最適化です(実行する命令が1つ少なくなります)。したがって、実際には改善が見られない可能性があります。
要約すると、はい、それは役に立ちますが、決定論的ではありません。
少なくともJavaでは、ブードゥープログラミングではありません。次のようなものを使用してJavaでオブジェクトを作成する場合
Foo bar = new Foo();
2つのことを行います。1つはオブジェクトへの参照を作成すること、もう1つはFooオブジェクト自体を作成することです。その参照または別の参照が存在する限り、特定のオブジェクトをgcすることはできません。ただし、その参照にnullを割り当てると...
bar = null ;
そして、他にオブジェクトへの参照がないと仮定すると、次にガベージコレクターが通過するときに、オブジェクトは解放され、gcで使用できます。
場合によります。
一般的に言えば、オブジェクトへの参照を短くすると、オブジェクトがより速く収集されます。
メソッドの実行に2秒かかり、メソッドの実行から1秒後にオブジェクトが不要になった場合は、そのメソッドへの参照をすべてクリアするのが理にかなっています。GCが1秒後もオブジェクトが参照されていることを確認した場合、次回は1分ほどでオブジェクトをチェックする可能性があります。
とにかく、デフォルトですべての参照をnullに設定することは、私にとって時期尚早の最適化であり、メモリ消費を測定可能に減少させる特定のまれなケースを除いて、誰もそれを行うべきではありません。
保持されているオブジェクトが非常に大きい場合を除いて、変数をスコープ外にするのではなく、明示的にnullに参照を設定しても、ガベージコレクターには役立ちません。作業が終了したらすぐにnullに設定することをお勧めします。
通常、参照をnullに設定することは、このオブジェクトが完全に実行されるコードのREADERを意味し、これ以上心配する必要はありません。
同様の効果は、追加のブレースのセットを挿入することにより、より狭い範囲を導入することによって達成できます。
{
int l;
{ // <- here
String bigThing = ....;
l = bigThing.length();
} // <- and here
}
これにより、ネストされた中括弧を残した直後にbigThingをガベージコレクションできます。
public class JavaMemory {
private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);
public void f() {
{
byte[] data = new byte[dataSize];
//data = null;
}
byte[] data2 = new byte[dataSize];
}
public static void main(String[] args) {
JavaMemory jmp = new JavaMemory();
jmp.f();
}
}
上記のプログラムはスローしOutOfMemoryError
ます。コメントを外すdata = null;
と、OutOfMemoryError
が解決されます。未使用の変数をnullに設定することは常に良い習慣です
OPは次のようなものを参照していると思います。
private void Blah()
{
MyObj a;
MyObj b;
try {
a = new MyObj();
b = new MyObj;
// do real work
} finally {
a = null;
b = null;
}
}
この場合、VMは、とにかくスコープを離れるとすぐに、GCのマークを付けませんか?
または、別の観点から、アイテムを明示的にnullに設定すると、スコープ外になった場合よりも前にGCが実行されますか?その場合、VMは、メモリが必要ないときにオブジェクトのGCに時間を費やす可能性があります。これにより、より早くGCが実行されるため、CPU使用率のパフォーマンスが低下します。
null
てGCの動作に影響を与えるとはまったく思いません。
「状況によります」
Javaについてはわかりませんが、.net(C#、VB.net ...)では、通常、オブジェクトが不要になったときにnullを割り当てる必要はありません。
ただし、「通常は不要」であることに注意してください。
コードを分析することにより、.netコンパイラは変数の存続期間を適切に評価し、オブジェクトが使用されなくなったことを正確に判断します。したがって、obj = nullと書くと、実際にはobjがまだ使用されているように見える場合があります...この場合、nullを割り当てるのは逆効果です。
nullを割り当てることが実際に役立つ場合がいくつかあります。1つの例は、長時間実行される巨大なコード、別のスレッドで実行されているメソッド、またはループがある場合です。このような場合は、nullを割り当てて、GCが使用されなくなったことを簡単に認識できるようにすることが役立つ場合があります。
これには厳格なルールはありません。上記の手順に従って、コードにnull割り当てを配置し、プロファイラーを実行して、何らかの形で役立つかどうかを確認します。おそらく、メリットが見られない可能性があります。
最適化しようとしているのが.netコードの場合、私の経験では、DisposeメソッドとFinalizeメソッドに十分注意する方が、nullを気にするよりも実際に有益です。
トピックに関するいくつかの参照:
http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx
http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx
参照を無効にする方がわずかに効率的だったとしても、これらの醜い無効化でコードをペッパーしなければならないという醜い価値はありますか?それらは雑然としていて、それらを含むインテントコードをあいまいにするだけです。
ガベージコレクターをアウトスマート化しようとするよりも最適化の候補がない、そのまれなコードベース(よりレアなのは、それをアウトスマート化することに成功した開発者です)。あなたの努力は、その代わりに、そのくだらないXmlパーサーを捨てるか、計算をキャッシュする機会を見つけるために、他の場所でよりよく使われるでしょう。これらの最適化は、定量化が容易になり、コードベースをノイズで汚す必要がなくなります。
プログラムの将来の実行では、一部のデータメンバーの値を使用して、プログラムの外部に表示される出力をコンピューター化します。プログラムへの将来の(そして予測不可能な)入力に応じて、他のものが使用される場合と使用されない場合があります。他のデータメンバーは使用されないことが保証される場合があります。これらの未使用データに割り当てられたメモリを含むすべてのリソースが無駄になります。ガベージコレクター(GC)の仕事は、その無駄なメモリを排除することです。GCが必要なものを排除することは悲惨なことであるため、使用されるアルゴリズムは保守的であり、厳密な最小値よりも多くを保持している可能性があります。ヒューリスティックな最適化を使用して速度を向上させる場合がありますが、実際には必要のないアイテムを保持する必要があります。GCが使用する可能性のある多くの潜在的なアルゴリズムがあります。したがって、プログラムに変更を加える可能性があります。プログラムの正確性には影響しませんが、それでもGCの動作に影響を与え、同じジョブを実行するために実行を高速化するか、未使用のアイテムをより早く特定する可能性があります。したがって、この種の変更は、unusddオブジェクト参照をに設定しますnull
、理論的には必ずしもブードゥーではありません。
ブードゥーですか?伝えられるところによると、これを行うJavaライブラリコードの一部があります。そのコードの作成者は、平均的なプログラマーよりもはるかに優れており、ガベージコレクターの実装の詳細を知っているプログラマーを知っているか、協力しています。つまり、それは時々利益があることを示唆しています。
最適化があると言ったように、つまり、JVMは変数が最後に使用された場所を認識しており、変数によって参照されるオブジェクトは、この最後のポイントの直後にGCできます(現在のスコープで実行中)。したがって、ほとんどの場合、参照を無効にしてもGCには役立ちません。
しかし、「縁故主義」(または「浮遊ゴミ」)の問題を回避することは役立つ場合があります(詳細はこちらを読むか、ビデオをご覧ください)。問題が存在するのは、ヒープが古い世代と若い世代に分割され、適用されるGCメカニズムが異なるためです。マイナーGC(高速で若い世代をクリーンアップするために頻繁に発生します)とメジャーGc(古い世代をクリーンアップするための一時停止が長くなります)。「縁故主義」は、すでに古い世代に所有されているゴミによって参照されている場合、若い世代のゴミを収集することを許可しません。
GCが問題を解決するまで、プロモートされたノードはすべて、後続のすべてのノードをプロモートするため、これは「病的」です。
縁故主義を回避するために、削除されることになっているオブジェクトからの参照を無効にすることをお勧めします。この手法がJDKクラスに適用されていることがわかります:LinkedListおよびLinkedHashMap
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
// ...
}