Javaでオブジェクトをnullに割り当てると、ガベージコレクションに影響しますか?


85

nullJavaで未使用のオブジェクト参照を割り当てると、ガベージコレクションプロセスが測定可能な方法で改善されますか?

私のJava(およびC#)の経験から、仮想マシンやJITコンパイラーを試してみるのは直感に反することが多いことがわかりましたが、同僚がこの方法を使用しているのを見て、これが良い習慣かどうか知りたいです。アップまたはそれらのブードゥープログラミングの迷信の1つ?

回答:


66

通常、いいえ。

しかし、すべてのものと同様に、それは状況によって異なります。最近の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;
}

機能的に同等です。


7
また、大量のメモリを使用したり、実行に時間がかかったりする可能性のあるメソッド内、または長時間実行される可能性のあるSwingアプリケーションのコード内で参照をnullにすることもできます。短いメソッドまたは短命のオブジェクトでは、余分な混乱を招くコードの価値はありません。
ジョンガードナー

1
それで、オブジェクトをnullにすると、ガベージコレクターはそのオブジェクトをすぐに収集しますか?
ムハンマドババール2013年

1
いいえ。ガベージコレクターは定期的に実行され、参照変数が指しているオブジェクトがないかどうかを確認します。それらをnullに設定してから呼び出すとSystem.gc()、削除されます(そのオブジェクトを指す他の参照がない場合)。
JavaTechnical 2014

2
@JavaTechnical私が知っているように、System.gc()を呼び出すことによってガベージコレクションが開始されることは保証されていません。
ジャック

12

私の経験では、多くの場合、人々は必要性からではなく、妄想から参照を無効にします。簡単なガイドラインは次のとおりです。

  1. オブジェクトAがオブジェクトB参照していてこの参照が不要になりオブジェクトAがガベージコレクションの対象にならない場合は、フィールドを明示的に無効にする必要があります。とにかく囲んでいるオブジェクトがガベージコレクションを取得している場合は、フィールドをnullにする必要はありません。dispose()メソッドでフィールドを無効にすることは、ほとんどの場合役に立ちません。

  2. メソッドで作成されたオブジェクト参照を無効にする必要はありません。メソッドが終了すると、それらは自動的にクリアされます。このルールの例外は、非常に長いメソッドまたは大規模なループで実行していて、メソッドが終了する前に一部の参照がクリアされるようにする必要がある場合です。繰り返しますが、これらのケースは非常にまれです。

ほとんどの場合、参照を無効にする必要はありません。ガベージコレクターの裏をかくことは無意味です。あなたはただ非効率的で読めないコードになってしまうでしょう。


11

良い記事は、今日のコーディングの恐怖です。

GCの動作は、ポインタを持たないオブジェクトを検索することです。検索の領域は、ヒープ/スタックおよびその他のスペースです。したがって、変数をnullに設定すると、実際のオブジェクトは誰にもポイントされないため、GCされる可能性があります。

ただし、GCはその瞬間に実行されない可能性があるため、実際には何も購入していない可能性があります。ただし、メソッドが(実行時間の点で)かなり長い場合は、GCがそのオブジェクトを収集する可能性が高くなるため、それだけの価値がある可能性があります。

この問題はコードの最適化でも複雑になる可能性があります。変数をnullに設定した後で変数を使用しない場合は、値をnullに設定する行を削除するのが安全な最適化です(実行する命令が1つ少なくなります)。したがって、実際には改善が見られない可能性があります。

要約すると、はい、それは役に立ちますが、決定論的ではありません



refをnullに設定する行を削除する安全な最適化の良い点。
asgs 2017

8

少なくともJavaでは、ブードゥープログラミングではありません。次のようなものを使用してJavaでオブジェクトを作成する場合

Foo bar = new Foo();

2つのことを行います。1つはオブジェクトへの参照を作成すること、もう1つはFooオブジェクト自体を作成することです。その参照または別の参照が存在する限り、特定のオブジェクトをgcすることはできません。ただし、その参照にnullを割り当てると...

bar = null ;

そして、他にオブジェクトへの参照がないと仮定すると、次にガベージコレクターが通過するときに、オブジェクトは解放され、gcで使用できます。


12
ただし、スコープ外に出ても、余分なコード行がなくても同じことができます。メソッドに対してローカルである場合、必要はありません。メソッドを終了すると、オブジェクトはGCの対象になります。
duffymo 2009年

2
良い。ここで、ポップクイズの場合、それだけでは不十分なコードの例を作成します。ヒント:それらは存在します。
チャーリーマーティン

7

場合によります。

一般的に言えば、オブジェクトへの参照を短くすると、オブジェクトがより速く収集されます。

メソッドの実行に2秒かかり、メソッドの実行から1秒後にオブジェクトが不要になった場合は、そのメソッドへの参照をすべてクリアするのが理にかなっています。GCが1秒後もオブジェクトが参照されていることを確認した場合、次回は1分ほどでオブジェクトをチェックする可能性があります。

とにかく、デフォルトですべての参照をnullに設定することは、私にとって時期尚早の最適化であり、メモリ消費を測定可能に減少させる特定のまれなケースを除いて、誰もそれを行うべきではありません。


6

保持されているオブジェクトが非常に大きい場合を除いて、変数をスコープ外にするのではなく、明示的にnullに参照を設定しても、ガベージコレクターには役立ちません。作業が終了したらすぐにnullに設定することをお勧めします。

通常、参照をnullに設定することは、このオブジェクトが完全に実行されるコードのREADERを意味し、これ以上心配する必要はありません。

同様の効果は、追加のブレースのセットを挿入することにより、より狭い範囲を導入することによって達成できます。

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

これにより、ネストされた中括弧を残した直後にbigThingをガベージコレクションできます。


良い選択肢。これにより、変数を明示的にnullに設定したり、一部のIDEがnull割り当てが使用されていないことを警告したりすることを回避できます。
jk7 2018年

5
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に設定することは常に良い習慣です


つまり、藁の議論の世界への確固たる進出であるimoです。変数をnullに設定するとその特定のケースで問題が解決する状況を作成できるからといって(そして、そのようにしたとは確信していません)、nullに設定することがすべての場合に良い考えであるとは限りません。コードを追加して、使用しなくなったすべての変数をnullに設定します。その直後にスコープから外れた場合でも、コードが膨張し、コードの保守性が低下します。
RHSeeger 2009年

変数データが​​スコープ外のときにbyte []配列のガベージコレクションが行われないのはなぜですか?
Grav 2011年

2
@Grav:スコープ外になると、ガベージコレクターがローカルオブジェクトを収集することが保証されません。ただし、nullに設定すると、OutOfMemory例外が発生しなくなります。これは、OutOfMemory例外をスローする前にGCが実行されることが保証されており、オブジェクトがnullに設定されている場合は収集可能であるためです。
Stefanos T.


4

私はビデオ会議アプリケーションに一度取り組んでいましたが、オブジェクトが不要になったらすぐに参照をnullにするのに時間をかけたときに、パフォーマンスに大きな大きな違いがあることに気づきました。これは2003年から2004年のことで、GCがさらにスマートになったと想像できます。私の場合、毎秒何百ものオブジェクトがスコープから出入りしていたので、定期的に起動したときにGCに気づきました。ただし、オブジェクトをnullにするように指示した後、GCはアプリケーションの一時停止を停止しました。

だからそれはあなたが何をしているかによります...


2

はい。

「実用的なプログラマー」p.292より:

参照をNULLに設定することにより、オブジェクトへのポインターの数を1つ減らします...(これにより、ガベージコレクターはオブジェクトを削除できます)


これは、参照カウントアルゴリズムの場合にのみ適用できると思います。使用中ですか、それ以外の場合もありますか?
nrj 2009年

3
このアドバイスは、現代のガベージコレクターにとっては時代遅れです。参照カウントGCには、循環参照などの重大な問題があります。
ローレンスドル

マークアンドスイープGCでも同様です。おそらく他のすべてで。参照が1つ少ないという事実は、参照がカウントされることを意味するわけではありません。tweaktによる答えはこれを説明しています。NULLに設定するよりも、スコープを縮小することをお勧めします。

1

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使用率のパフォーマンスが低下します。


GCが実行される時間は非決定論的です。オブジェクトを設定しnullてGCの動作に影響を与えるとはまったく思いません。
harto 2009

0

状況によります

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


0

参照を無効にする方がわずかに効率的だったとしても、これらの醜い無効化でコードをペッパーしなければならないという醜い価値はありますか?それらは雑然としていて、それらを含むインテントコードをあいまいにするだけです。

ガベージコレクターをアウトスマート化しようとするよりも最適化の候補がない、そのまれなコードベース(よりレアなのは、それをアウトスマート化することに成功した開発者です)。あなたの努力は、その代わりに、そのくだらないXmlパーサーを捨てるか、計算をキャッシュする機会を見つけるために、他の場所でよりよく使われるでしょう。これらの最適化は、定量化が容易になり、コードベースをノイズで汚す必要がなくなります。


0

プログラムの将来の実行では、一部のデータメンバーの値を使用して、プログラムの外部に表示される出力をコンピューター化します。プログラムへの将来の(そして予測不可能な)入力に応じて、他のものが使用される場合と使用されない場合があります。他のデータメンバーは使用されないことが保証される場合があります。これらの未使用データに割り当てられたメモリを含むすべてのリソースが無駄になります。ガベージコレクター(GC)の仕事は、その無駄なメモリを排除することです。GCが必要なものを排除することは悲惨なことであるため、使用されるアルゴリズムは保守的であり、厳密な最小値よりも多くを保持している可能性があります。ヒューリスティックな最適化を使用して速度を向上させる場合がありますが、実際には必要のないアイテムを保持する必要があります。GCが使用する可能性のある多くの潜在的なアルゴリズムがあります。したがって、プログラムに変更を加える可能性があります。プログラムの正確性には影響しませんが、それでもGCの動作に影響を与え、同じジョブを実行するために実行を高速化するか、未使用のアイテムをより早く特定する可能性があります。したがって、この種の変更は、unusddオブジェクト参照をに設定しますnull理論的には必ずしもブードゥーではありません。

ブードゥーですか?伝えられるところによると、これを行うJavaライブラリコードの一部があります。そのコードの作成者は、平均的なプログラマーよりもはるかに優れており、ガベージコレクターの実装の詳細を知っているプログラマーを知っているか、協力しています。つまり、それ時々利益あることを示唆しています。


0

最適化があると言ったように、つまり、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
    // ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.