web-dev-qa-db-ja.com

System.gc()を呼び出すのが悪い習慣なのはなぜですか?

回答 方法に関する質問 Javaのオブジェクトを強制的に解放する (男は1.5GB HashMapをクリアしていました)System.gc()で、手動でSystem.gc()を呼び出すのは悪い習慣だと言われました、しかし、コメントは完全に説得力がありませんでした。加えて、誰も私の答えに賛成票を投じたり、反対票を投じたりするようには見えませんでした。

悪い習慣だと言われましたが、ガベージコレクターを実行しても体系的に世界が停止することはなく、JVMによってヒントとしてのみ有効に使用できると言われたので、損失で。

JVMは通常、メモリを再利用する必要があるときに、JVMがあなたよりもよく知っていることを理解しています。また、数キロバイトのデータを心配するのは馬鹿げていることも理解しています。また、メガバイトのデータでさえ、数年前のデータではないことも理解しています。それでも、1.5ギガバイトですか?そして、あなたはknow1.5 GBのデータがメモリ内にぶら下がっていることを知っています。暗闇でのショットのようではありません。 System.gc()は体系的に悪いのですか、それとも大丈夫になるポイントがありますか?

したがって、質問は実際には二重です。

  • System.gc()を呼び出すのはなぜ悪い習慣ですか?特定の実装の下でのJVMへの単なるヒントですか、それとも常に完全なコレクションサイクルですか?世界を止めずに作業を行うことができるガベージコレクター実装は本当にありますか?私の answer へのコメントで人々が行ったさまざまな主張に光を当ててください。
  • しきい値はどこですか? System.gc()を呼び出すことはnever良いアイデアですか、それとも受け入れられる場合がありますか?もしそうなら、それらの時間は何ですか?
308
zneak

誰もがSystem.gc()を避けるように常に言っている理由は、それが根本的に壊れたコードのかなり良い指標であることです。正確性のためにそれに依存しているコードは確かに壊れています。パフォーマンスに依存しているものはほとんど壊れています。

実行しているガベージコレクターの種類がわかりません。確かに「世界を止めない」ではないものもありますが、一部のJVMはそれほどスマートではないか、さまざまな理由(おそらく電話に接続しているのでしょうか)ではありません。しないで何をするのかわかりません。

また、何もすることが保証されていません。 JVMは要求を完全に無視する場合があります。

「それが何をするのかわからない」、「助けになるかどうかわからない」、「とにかくそれを呼ぶ必要はない」の組み合わせが、人々が一般的にそうすることを強いる理由です。あなたはそれを呼び出すべきではありません。 「これを使用するかどうかを尋ねる必要がある場合、使用しないでください」の場合だと思います


編集他のスレッドからのいくつかの懸念に対処する:

リンクしたスレッドを読んだ後、指摘したいことがいくつかあります。まず、誰かがgc()を呼び出すとシステムにメモリを返す可能性があることを提案しました。それは確かに必ずしも真実ではありません-Javaヒープ自体はJava割り当てとは無関係に成長します。

同様に、JVMはメモリ(数十メガバイト)を保持し、必要に応じてヒープを拡大します。 Javaオブジェクトを解放しても、必ずしもそのメモリがシステムに返されるとは限りません。将来のJava割り当てに使用するために割り当てられたメモリを保持することは完全に自由です。

System.gc()が何もしない可能性があることを示すには、次を表示します。

http://bugs.Sun.com/view_bug.do?bug_id=6668279

特に、 -XX:DisableExplicitGC VMオプションがあります。

236

system.gc()mayを呼び出しても何も実行されず、実行するガベージコレクタを「必要とする」コードが破損することは既に説明されています。

ただし、System.gc()を呼び出すのは悪い習慣であるという実際的な理由は、非効率的であるということです。そして最悪の場合、それは恐ろしく非効率的!説明させてください。

一般的なGCアルゴリズムは、ヒープ内のすべての非ガベージオブジェクトをトラバースし、アクセスされていないオブジェクトはガベージであると推測してガベージを識別します。これから、ガベージコレクションの全作業を、ライブデータの量に比例する1つの部分とガベージの量に比例する別の部分でモデル化できます。つまり、work = (live * W1 + garbage * W2)

ここで、シングルスレッドアプリケーションで次のことを行うとします。

System.gc(); System.gc();

最初の呼び出しは(予測)(live * W1 + garbage * W2)の作業を行い、未処理のゴミを取り除きます。

2番目の呼び出しは(live* W1 + 0 * W2)の作業を行い、何も回収しません。つまり、(live * W1)の作業を完了し、まったく何も達成しませんでした

ガベージ単位を収集するために必要な作業量として、コレクターの効率をモデル化できます。つまり、efficiency = (live * W1 + garbage * W2) / garbage。したがって、GCを可能な限り効率的にするには、GCを実行するときにgarbageの値をmaximizeする必要があります。つまり、ヒープがいっぱいになるまで待ちます。 (また、ヒープをできるだけ大きくします。しかし、それは別のトピックです。)

アプリケーションが(System.gc()を呼び出すことによって)干渉しない場合、GCは実行する前にヒープがいっぱいになるまで待機します。これにより、効率的なガベージコレクションが行われます。1。しかし、アプリケーションがGCを強制的に実行すると、ヒープがいっぱいにならず、その結果、ガベージが非効率的に収集される可能性があります。また、アプリケーションが頻繁にGCを強制するほど、GCの効率は低下します。

注:上記の説明は、典型的な最新のGCがヒープを「スペース」に分割し、GCが動的にヒープを拡張し、アプリケーションの非ガベージオブジェクトのワーキングセットが異なるなどの事実を示しています。それでも、同じ基本原則がすべての真のガベージコレクターに適用されます2。 GCを強制的に実行するのは非効率的です。


1-これは、「スループット」コレクターの仕組みです。 CMSやG1などの並行コレクターは、異なる基準を使用して、ガベージコレクターをいつ開始するかを決定します。

2-参照カウントのみを使用するメモリマネージャーも除外しますが、現在のJava実装ではそのアプローチを使用していません...十分な理由があります。

143
Stephen C

多くの人がこれをしないように言っているようです。同意しません。レベルのロードなどの大規模なロードプロセスの後、次のことを信じている場合:

  1. 到達不能でgcされていない可能性のあるオブジェクトがたくさんあります。そして
  2. この時点で、ユーザーは小さな減速に耐えることができると思います

system.gc()を呼び出しても害はありません。私はそれをc/c ++ inlineキーワードのように見ます。開発者であるあなたが、時間/パフォーマンスは通常ほど重要ではないと判断し、その一部はメモリの再利用に使用できると判断したことは、gcへの単なるヒントです。

何かをすることに頼らないというアドバイスは正しい。動作に依存しないでください。ただし、収集するのに許容できる時間であるというヒントを与えることは、まったく問題ありません。ユーザーがプログラムと積極的にやり取りしているとき(ゲームのレベルのときなど)よりも、コードの重要ではない場所(画面の読み込み)で時間を無駄にしたいです。

force コレクション:特定のオブジェクトリーク(ネイティブコードまたは大規模で複雑なコールバックインタラクションのいずれか)を見つけようとするときがあります。ああ、Matlabを一見するようなUIコンポーネント)これは本番コードでは決して使用すべきではありません。

43
KitsuneYMG

人々はなぜ使用しないのかを説明しているので、それを使用すべきいくつかの状況を説明します。

(以下のコメントは、CMSコレクターを搭載したLinux上で実行されるHotspotに適用されます。実際、System.gc()は実際には常に完全なガベージコレクションを呼び出すと確信しています).

  1. アプリケーションを起動する最初の作業の後、メモリ使用量がひどい状態になる可能性があります。あなたの終身世代の半分はごみだらけである可能性があります。つまり、最初のCMSにずっと近いということです。それが重要なアプリケーションでは、System.gc()を呼び出してヒープをライブデータの開始状態に「リセット」することは悪い考えではありません。

  2. #1と同じ行に沿って、ヒープ使用量を綿密に監視する場合、ベースラインメモリ使用量を正確に読み取る必要があります。アプリケーションのアップタイムの最初の2分間がすべて初期化である場合、完全なgcを前もって強制しない限り(つまり「提案」)、データは台無しになります。

  3. 実行中に終身世代に何も昇格させないように設計されたアプリケーションがあります。ただし、古い世代に自動的に移動するために、それほど大きくないデータを事前に初期化する必要がある場合があります。すべてを設定した後にSystem.gc()を呼び出さない限り、昇格する時が来るまでデータは新しい世代に置かれます。スーパーデュパーの低レイテンシ、低GCアプリケーションは、通常の動作中にこれらのオブジェクトをプロモートするための巨大な(もちろん比較的相対的な)レイテンシペナルティで突然ヒットします。

  4. メモリリークの存在を確認するために、実稼働アプリケーションでSystem.gc呼び出しを使用できると便利な場合があります。時間Xのライブデータのセットが時間Yのライブデータのセットに対して特定の比率で存在する必要があることがわかっている場合、System.gc()を時間Xと時間Yで呼び出し、メモリ使用量を比較すると便利です。

29
JT.

GCの効率は多くのヒューリスティックに依存しています。たとえば、一般的なヒューリスティックは、オブジェクトへの書き込みアクセスが通常、少し前に作成されたオブジェクトで発生することです。もう1つは、多くのオブジェクトの寿命が非常に短いことです(一部のオブジェクトは長時間使用されますが、多くのオブジェクトは作成後数マイクロ秒で破棄されます)。

System.gc()を呼び出すことは、GCをキックするようなものです。つまり、「慎重に調整されたすべてのパラメーター、賢明な組織、物事がスムーズに、うまくいくように、オブジェクトを割り当てて管理するためのすべての努力、すべてを落として、ゼロから始める」ということです。 mayパフォーマンスは向上しますが、ほとんどの場合はdegradesパフォーマンスです。

確実にSystem.gc()を使用するには(*)、GCがどのように動作するかを細かく知る必要があります。別のベンダーのJVM、同じベンダーの次のバージョン、または同じJVMを使用しているが、コマンドラインオプションがわずかに異なる場合、このような詳細はかなり変化する傾向があります。そのため、これらすべてのパラメーターを制御する特定の問題に対処したい場合を除いて、良いアイデアとなることはめったにありません。したがって、「悪い習慣」の概念:それは禁止されていません、メソッドは存在しますが、それはめったに報われません。

(*)ここで効率について話しています。 System.gc()は決してbreak正しいJavaプログラムではありません。 JVMが他の方法で取得できなかった余分なメモリを呼び出すことはありません。OutOfMemoryErrorをスローする前に、JVMは、最後の手段としてであってもSystem.gc()のジョブを実行します。

9
Thomas Pornin

時々(あまりない!)過去、現在、未来のメモリ使用量について、実行時よりも本当によく知っていることがあります。これはあまり頻繁に起こることではなく、通常のページが提供されている間はWebアプリケーションでは決してないと主張します。

何年も前に、レポートジェネレーターで作業しています。

  • シングルスレッドを持っていた
  • キューから「レポートリクエスト」を読む
  • データベースからレポートに必要なデータをロードしました
  • レポートを生成してメールで送信しました。
  • 永久に繰り返され、未処理の要求がないときにスリープします。
  • レポート間でデータを再利用せず、キャッシングもしませんでした。

第一に、それはリアルタイムではなく、ユーザーがレポートを待つことを期待していたため、GC実行中の遅延は問題ではありませんでしたが、要求よりも速いレートでレポートを作成する必要がありました。

上記のプロセスの概要を見ると、明らかです。

  • 次のリクエストの処理がまだ開始されていないため、レポートが電子メールで送信された直後のライブオブジェクトはほとんどないことがわかっています。
  • ガベージコレクションサイクルの実行コストがライブオブジェクトの数に依存することはよく知られています。ガベージの量はGC実行のコストにほとんど影響しません。
  • キューが空のときは、GCを実行することをお勧めします。

したがって、要求キューが空のときはいつでもGCを実行することは明らかに価値がありました。これにはマイナス面はありませんでした。

これはGCの実行に適した時間であることがわかっているため、各レポートが電子メールで送信された後にGCを実行する価値があるかもしれません。ただし、コンピューターに十分なRAMがある場合は、GCの実行を遅らせることでより良い結果が得られます。

someレポートごとに強制GCを有効にする顧客向けに、インストールごとにこの動作を設定しました大幅に高速化レポートの保護を強化しました。 (これはサーバーのメモリ不足と他の多くのプロセスを実行しているためと思われるので、適切な時間で強制的にGCを実行するとページングが減少します。)

ワークキューが空になるたびに強制的にGCを実行するというメリットのないインストールは検出されませんでした。

しかし、明確にするために、上記は一般的なケースではありません。

7
Ian Ringrose

これは非常に面倒な質問であり、言語の有用性にもかかわらずJavaに反対する多くの人々に貢献していると感じています。

「System.gc」が何かをすることを信頼できないという事実は、信じられないほど困難であり、言語に対する「恐怖、不確実性、疑い」の感覚を簡単に引き起こすことができます。

多くの場合、重要なイベントが発生する前に意図的に引き起こしたメモリスパイクに対処するのは良いことです。これにより、ユーザーはプログラムが不適切に設計された/応答しないと考えるようになります。

ガベージコレクションを制御する能力は非常に優れた教育ツールであり、ガベージコレクションがどのように機能し、プログラムがそのデフォルトの動作と制御された動作を悪用する方法を人々が理解しやすくなります。

このスレッドの引数を見てみましょう。

  1. 効率が悪い:

多くの場合、プログラムは何も実行していない可能性があり、設計された方法のために何も実行していないことがわかります。たとえば、大きな待機メッセージボックスである種の長い待機を行っている可能性があります。最後に、ガベージを収集するための呼び出しを追加する場合があります。長い待ち時間ですが、より重要な操作の途中でgcが動作するのを防ぎます。

  1. これは常に悪い習慣であり、壊れたコードを示しています。

私は同意しません、あなたがどんなガベージコレクターを持っているかは関係ありません。その仕事はゴミを追跡し、それをきれいにすることです。

使用の重要性が低い時間にgcを呼び出すことにより、実行中の特定のコードに依存している代わりにガベージを収集することに決めた場合に、実行の確率を減らすことができます。

確かに、それはあなたが望むまたは期待する方法で動作しないかもしれませんが、あなたがそれを呼び出したいとき、あなたは何も起こっていないことを知っており、ユーザーは遅さ/ダウンタイムを許容します。 System.gcが機能する場合、素晴らしい!そうでない場合は、少なくとも試してみました。ガベージコレクターが、手動で呼び出された場合にガベージコレクターがどのように動作するかについて恐ろしく予期しない固有の副作用を持たない限り、単純にマイナス面はありません。

  1. 一般的なユースケースではありません。

信頼できる方法ではありませんが、システムがそのように設計されている場合は、ユースケースです。信号機を作成して、信号機のボタンの一部/すべてが何もしないようにすること、ボタンが最初にある理由を疑問視すること、javascriptにガベージコレクション機能がないため、それを精査しないでください。

  1. 仕様では、System.gc()はGCを実行するヒントであり、VMは自由に無視できると述べています。

「ヒント」とは何ですか? 「無視」とは何ですか?コンピューターは単にヒントをとったり、何かを無視したりすることはできません。システムの意図によって導かれる動的な可能性のある厳密な動作パスがあります。適切な回答には、実装レベルでガベージコレクターが実際に実行している処理が含まれます。これにより、要求時に収集が実行されなくなります。機能は単にnopですか?満たさなければならない条件はありますか?これらの条件は何ですか?

現状では、JavaのGCは多くの場合、あなたが信用していない怪物のように見えます。それがいつ来るのかわからない、何をするのか、どうするのかわからない。ガベージコレクションが命令ごとにどのように機能するかをよく知っている専門家もいると思いますが、大多数は単純に機能することを望み、不透明なアルゴリズムを信頼して作業を行うのはイライラします。

何かを読んだり何かを教えたり、実際にそれを実装したり、システム間での違いを確認したり、ソースコードを見なくてもそれで遊んだりすることには大きなギャップがあります。これにより、マスター/理解/制御の自信と感覚が生まれます。

要約すると、答えには固有の問題があります。「この機能は何もしない可能性があります。いつ機能するのか、いつ機能しないのか、なぜ機能しない、またはするのかを詳しく説明しません。背後にある意図が合理的であっても、それを試みることは単に哲学に反することをしばしば暗示している」。

Java GCが正常に動作する場合とそうでない場合がありますが、理解するために、どの方向に進んで何ができるかを包括的に把握するのは本当に難しいですGCが行うこととしないことを信頼するため、言語の目的は哲学的な範囲まで行動を制御することであるため、単純に言語を信用しすぎます(プログラマー、特に初心者が特定の存在から危機に陥るのは簡単です)システム/言語の動作)許容することができます(できない場合は、必要になるまで言語を使用しません)、および制御できない理由が不明なため制御できないものそれらは本質的に有害です。

6
Dmitry

たぶん私は安っぽいコードを書きますが、Eclipseおよびnetbeans IDEでゴミ箱アイコンをクリックするのは「良い習慣」であることに気付きました。

3
Ryan Fernandes

まず、仕様と現実には違いがあります。仕様では、System.gc()はGCを実行するヒントであり、VMは自由に無視できると述べています。現実には、VMはnever System.gc()への呼び出しを無視します。

GCの呼び出しには、呼び出しに対する重要なオーバーヘッドが伴います。これを何らかのランダムな時点で実行すると、努力に対する報酬が得られない可能性があります。一方、自然にトリガーされるコレクションは、通話のコストを回収する可能性が非常に高くなります。 GCを実行する必要があることを示す情報がある場合は、System.gc()を呼び出して、利点を確認できます。ただし、System.gc()を呼び出すかどうか、いつ呼び出すかを理解するのに十分な情報があるとは考えにくいため、これは少数のEdgeの場合にのみ発生するというのが私の経験です。

IDEでゴミ箱にぶつかる例を次に示します。あなたが会議に行くなら、なぜそれを打ってはいけません。オーバーヘッドはあなたに影響を与えませんし、戻ったときにヒープがクリーンアップされるかもしれません。実稼働システムでこれを実行すると、頻繁に収集を呼び出すと、停止します。 RMIによって行われるような時折の呼び出しでさえ、パフォーマンスを混乱させる可能性があります。

2
Kirk

はい、System.gc()を呼び出しても、それが実行されることは保証されません。これは、JVMへの要求であり、無視される場合があります。ドキュメントから:

Gcメソッドの呼び出しは、Java仮想マシンが未使用オブジェクトのリサイクルに努力を費やすことを示唆しています

通常、自動メモリ管理はgcを実行するタイミングがあなたよりもよくわかっているので、それを呼び出すことはほとんど常に悪い考えです。空きメモリの内部プールが少ない場合、またはOSがメモリの返却を要求した場合に、そうします。

knowが役立つ場合は、System.gc()を呼び出すこともできます。つまり、展開プラットフォームで両方のシナリオの動作を徹底的にテストおよび測定し、それが役立つことを示すことができます。ただし、gcは簡単に予測できないことに注意してください。1回の実行で助けになり、別の実行で傷つくことがあります。

1
tom
  1. オブジェクトはnew演算子を使用して動的に割り当てられるため、
    このようなオブジェクトがどのように破壊され、
    後の再割り当てのためにメモリが解放されました。

  2. C++などの一部の言語では、動的に割り当てられたオブジェクトは、削除演算子を使用して手動で解放する必要があります。

  3. Javaは異なるアプローチを取ります。自動的に割り当て解除を処理します。
  4. これを実現する手法は、ガベージコレクションと呼ばれます。これは次のように機能します。オブジェクトへの参照が存在しない場合、そのオブジェクトはもはや不要であると見なされ、オブジェクトが占有しているメモリを回収できます。 C++のようにオブジェクトを破棄する明示的な必要はありません。
  5. ガベージコレクションは、プログラムの実行中に散発的に(もしあれば)発生します。
  6. 使用されなくなった1つ以上のオブジェクトが存在するという理由だけでは発生しません。
  7. さらに、異なるJavaランタイム実装はガベージコレクションに対してさまざまなアプローチをとりますが、ほとんどの場合、プログラムの作成中にそれを考慮する必要はありません。
0
Tanmay Delhikar

私の経験では、System.gc()の使用は事実上プラットフォーム固有の最適化形式です(「プラットフォーム」はハードウェアアーキテクチャ、OS、JVMバージョン、および使用可能なRAMなどの実行時パラメーターの組み合わせです) )、その動作は、特定のプラットフォームで大まかに予測可能ではありますが、プラットフォーム間でかなり異なる可能性があります(そして、変わるでしょう)。

はい、System.gc()が(知覚される)パフォーマンスを改善するare状況があります。たとえば、アプリの一部で遅延が許容されるが、他の部分では許容されない場合です(上記のゲーム例では、レベル中ではなくレベルの開始時にGCを実行します)。

しかし、それが助けになるか、傷つくか(または何もしない)は、高度にプラットフォームに依存します(上記で定義)。

だから、最後のリゾートのプラットフォーム固有の最適化として有効だと思います(つまり、他のパフォーマンスの最適化では不十分な場合)。しかし、(特定のベンチマークなしで)役立つ可能性があると信じているからといって、決して呼び出すべきではありません。

0
sleske