Java で未使用のオブジェクト参照を割り当てるとnull
、ガベージ コレクション プロセスが測定可能な方法で改善されますか?
Java (および C#) の経験から、仮想マシンや JIT コンパイラを試して裏をかくのは直観に反することが多いことがわかりましたが、同僚がこの方法を使用しているのを見たことがあります。またはそれらのブードゥー教のプログラミングの迷信の 1 つですか?
Java で未使用のオブジェクト参照を割り当てるとnull
、ガベージ コレクション プロセスが測定可能な方法で改善されますか?
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;
}
機能的に同等です。
私の経験では、多くの場合、人々は必要からではなくパラノイアから参照を無効にします。以下に簡単なガイドラインを示します。
オブジェクト A がオブジェクト Bを参照していて、この参照が不要になり、オブジェクト A がガベージ コレクションの対象にならない場合は、フィールドを明示的に null にする必要があります。いずれにせよ、囲んでいるオブジェクトがガベージ コレクションを取得している場合は、フィールドを null にする必要はありません。dispose() メソッドでフィールドを null にすることは、ほとんどの場合役に立ちません。
メソッドで作成されたオブジェクト参照を無効にする必要はありません。メソッドが終了すると、それらは自動的にクリアされます。このルールの例外は、非常に長いメソッドまたは大規模なループで実行していて、メソッドの終了前にいくつかの参照をクリアする必要がある場合です。繰り返しますが、これらのケースは非常にまれです。
ほとんどの場合、参照を無効にする必要はありません。ガベージ コレクターの裏をかこうとしても無駄です。非効率的で判読できないコードになってしまうだけです。
良い記事は今日のコーディングホラーです。
GC の作業は、オブジェクトへのポインターを持たないオブジェクトを検索する方法であり、検索の領域はヒープ/スタックとそれらが持つその他のスペースです。そのため、変数を null に設定すると、実際のオブジェクトは誰にもポイントされなくなり、GC される可能性があります。
しかし、GC はその瞬間に実行されない可能性があるため、実際には何も購入していない可能性があります。ただし、メソッドが (実行時間の点で) かなり長い場合は、そのオブジェクトを GC で収集する可能性が高くなるため、価値があるかもしれません。
問題はコードの最適化でも複雑になる可能性があります。変数を null に設定した後に変数を使用しない場合、値を null に設定する行を削除するのが安全な最適化です (実行する命令が 1 つ少なくなります)。そのため、実際には改善されていない可能性があります。
要約すると、はい、それは役に立ちますが、決定論的ではありません。
少なくとも Java では、ブードゥー教のプログラミングではありません。次のようなものを使用してJavaでオブジェクトを作成すると
Foo bar = new Foo();
まず、オブジェクトへの参照を作成し、次に Foo オブジェクト自体を作成します。その参照または別の参照が存在する限り、特定のオブジェクトを gc することはできません。ただし、その参照に null を割り当てると...
bar = null ;
オブジェクトへの参照が他にないと仮定すると、オブジェクトは解放され、次にガベージ コレクターが通過したときに gc で使用できるようになります。
変数をスコープ外に出すだけでなく、null への参照を明示的に設定しても、ガベージ コレクターは役に立ちません。ただし、保持されているオブジェクトが非常に大きい場合を除きます。この場合、処理が終わったらすぐに null に設定することをお勧めします。
通常、null への参照を設定することは、このオブジェクトが完全に処理され、これ以上気にする必要がないコードの READER を意味します。
同様の効果は、中括弧の追加セットを入れてより狭い範囲を導入することで達成できます
{
int l;
{ // <- here
String bigThing = ....;
l = bigThing.length();
} // <- and here
}
これにより、ネストされたブレースを離れた直後に bigThing をガベージ コレクションできます。
場合によります。
一般的に言えば、オブジェクトへの参照を保持する時間が短いほど、オブジェクトの収集が速くなります。
メソッドの実行に 2 秒かかり、メソッドの実行から 1 秒後にオブジェクトが不要になった場合は、オブジェクトへの参照をすべてクリアするのが理にかなっています。GC が 1 秒後にオブジェクトがまだ参照されていることを確認した場合、次回は 1 分ほどでオブジェクトをチェックする可能性があります。
とにかく、デフォルトですべての参照を null に設定することは、時期尚早の最適化であり、メモリ消費が測定可能なほど減少する特定のまれなケースでない限り、誰もそれを行うべきではありません。
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 に設定することを常にお勧めします
ある時、ビデオ会議アプリケーションで作業していたときに、オブジェクトが不要になった時点ですぐに参照を null にすると、パフォーマンスに大きな違いがあることに気付きました。これは 2003 年から 2004 年にかけてのことで、それ以来 GC がさらにスマートになっているとしか思えません。私の場合、毎秒何百ものオブジェクトが範囲外に出入りしていたので、GC が定期的に起動したときに GC に気付きました。ただし、null オブジェクトを指定した後、GC はアプリケーションの一時停止を停止しました。
だから、それはあなたが何をするかによって異なります...
はい。
「実用的なプログラマー」p.292 から:
NULL への参照を設定することにより、オブジェクトへのポインターの数を 1 つ減らすことができます (これにより、ガベージ コレクターがオブジェクトを削除できるようになります)。
参照の無効化がわずかに効率的であったとしても、これらの醜い無効化をコードに追加する必要があるという醜い価値はありますか? それらは雑然として、それらを含むインテント コードを曖昧にするだけです。
これは、ガベージ コレクターの裏をかこうとする以外に最適化の候補がいないまれなコードベースです (ガベージ コレクターの裏をかくことに成功する開発者はまだまれです)。あなたの努力は、代わりに別の場所に費やされたほうがよいでしょう。つまり、その粗雑な Xml パーサーを捨てるか、計算をキャッシュする機会を見つけることです。これらの最適化は定量化が容易になり、コードベースをノイズで汚す必要がなくなります。
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 使用率のパフォーマンスが低下します。
「場合による」
Java についてはわかりませんが、.net (C#、VB.net...) では、通常、オブジェクトが不要になったときに null を割り当てる必要はありません。
ただし、「通常は不要」であることに注意してください。
コードを分析することにより、.net コンパイラは変数の有効期間を適切に評価し、オブジェクトがいつ使用されなくなったかを正確に判断します。したがって、obj=null と記述すると、実際には obj がまだ使用されているように見える場合があります。この場合、null を割り当てると逆効果です。
null を割り当てることが実際に役立つ場合がいくつかあります。1 つの例として、長時間実行される巨大なコードや、別のスレッドで実行されているメソッド、または何らかのループがある場合があります。そのような場合、GC がもう使用されていないことを簡単に認識できるように、null を割り当てると役立つ場合があります。
これには厳格なルールはありません。上記の場所に行って、コードに 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
プログラムの今後の実行では、一部のデータ メンバーの値を使用して、プログラムの外部に表示される出力を計算します。プログラムへの将来の(そして予測不可能な)入力に応じて、他のものは使用される場合と使用されない場合があります。他のデータ メンバーは、使用されないことが保証される場合があります。これらの未使用のデータに割り当てられたメモリを含むすべてのリソースが無駄になります。ガベージ コレクター (GC) の仕事は、その無駄なメモリを排除することです。GC が必要なものを排除するのは悲惨なことなので、使用されるアルゴリズムは保守的で、厳密な最小値以上のものを保持している可能性があります。実際には必要のないいくつかのアイテムを保持することを犠牲にして、ヒューリスティックな最適化を使用して速度を向上させる場合があります。GC が使用する可能性のあるアルゴリズムは多数あります。したがって、プログラムに変更を加える可能性があります。プログラムの正確性には影響しませんが、GC の動作に影響を与える可能性があり、同じジョブを実行するために高速に実行したり、未使用のアイテムをより早く特定したりします。この種の変更では、unusdd オブジェクト参照をnull
、理論的には必ずしもブードゥー教ではありません。
ブードゥーですか?これを行う Java ライブラリ コードの一部があると報告されています。そのコードの作成者は、平均的なプログラマーよりもはるかに優れており、ガベージ コレクターの実装の詳細を知っているプログラマーを知っているか、そのプログラマーと協力しています。つまり、時には利益があることを示唆しています。