17

JVMで実行されている2つの別々のキャッシュ(1つはサードパーティのライブラリによって制御されています)があり、それぞれがソフト参照を使用しています。ライブラリによって制御されるキャッシュよりも先に、JVMが制御されたキャッシュをクリアすることをお勧めします。SoftReferencejavadocは次のように述べています。

ソフトに到達可能なオブジェクトへのすべてのソフト参照は、仮想マシンがOutOfMemoryErrorをスローする前にクリアされていることが保証されます。それ以外の場合、ソフト参照がクリアされる時間や、さまざまなオブジェクトへのそのような参照のセットがクリアされる順序に制約はありません。ただし、仮想マシンの実装では、最近作成された、または最近使用されたソフト参照をクリアしないようにバイアスをかけることをお勧めします。

このクラスの直接インスタンスを使用して、単純なキャッシュを実装できます。このクラスまたは派生サブクラスは、より高度なキャッシュを実装するために、より大きなデータ構造で使用することもできます。ソフト参照の指示対象が強く到達可能である、つまり実際に使用されている限り、ソフト参照はクリアされません。したがって、洗練されたキャッシュは、たとえば、最近使用されたエントリがそれらのエントリへの強力な参照を保持し、残りのエントリがガベージコレクタの裁量で破棄されるようにすることで破棄されるのを防ぐことができます。

一般的なJVM実装、特にHotSpotは、実際にSoftReferencesをどのように処理しますか?仕様で推奨されているように、「最近作成された、または最近使用されたソフト参照のクリアにバイアスをかけている」のでしょうか。

4

5 に答える 5

8

調整可能であるように見えますが、そうではありません。同時マークスイープコレクターは、デフォルトのヒープの実装でハングします。デフォルトのヒープの実装はmust_clear_all_soft_refs()、明らかに。trueを実行する場合のみ_last_ditch_collectionです。

bool GenCollectedHeap::must_clear_all_soft_refs() {
  return _gc_cause == GCCause::_last_ditch_collection;
}

失敗した割り当ての通常の処理では、ヒープのdo_collectメソッドが3回連続して呼び出されますが、CollectorPolicy.cpp

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
                                                    bool   is_tlab) {

これは、収集を試み、再割り当てを試み、失敗した場合はヒープを拡張しようとし、最後の努力として、クリアリングソフト参照を収集しようとします。

最後のコレクションに関するコメントは非常にわかりやすいものです(そして、ソフト参照のクリアをトリガーする唯一のコレクションです)

  // If we reach this point, we're really out of memory. Try every trick
  // we can to reclaim memory. Force collection of soft references. Force
  // a complete compaction of the heap. Any additional methods for finding
  // free memory should be here, especially if they are expensive. If this
  // attempt fails, an OOM exception will be thrown.
  {
    IntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted

    gch->do_collection(true             /* full */,
                       true             /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  }

---明らかなことに応じて編集されましたが、ソフト参照ではなく弱参照を記述していました---

実際には、SoftReferencesは、JVMがガベージコレクションを回避しようとしたときに、ガベージコレクションが呼び出された場合にのみ「フォローされない」と思いOutOfMemoryErrorます。

SoftReferencesが4つのJava1.4ガベージコレクターすべて、および新しいG1コレクターと互換性を持つためには、到達可能性の決定のみに基づいて決定する必要があります。刈り取りと圧縮が行われるまでに、オブジェクトが到達可能かどうかを判断するには遅すぎます。これは、ヒープ内の空きメモリの可用性に基づいて到達可能性を決定するコレクション「コンテキスト」が存在することを示唆しています(ただし、必須ではありません)。このようなコンテキストは、SoftReferencesをフォローしようとする前に、sをフォローしていないことを示す必要があります。

OutOfMemoryError回避ガベージコレクションは、フルコレクションのストップザワールド方式で特別にスケジュールされているため、ヒープマネージャーがコレクションが発生する前に「フォローしない」フラグを設定するシナリオを想像するのは難しいことではありませんSoftReference

---わかりました、それで私は「このように働かなければならない」という答えは十分ではないと判断しました---

ソースコードからsrc/share / vm / gc_implementation / concurrentMarkSweep / vmCMSOperations.cpp(ハイライトは私のものです)

ガベージコレクションを実際に「実行」する操作:

  170 void VM_GenCollectFullConcurrent::doit() {

VMスレッドである方がよいです。そうでない場合、「プログラム」スレッドはガベージコレクションになります。

  171   assert(Thread::current()->is_VM_thread(), "Should be VM thread");

私たちはコンカレントコレクターなので、同時にスケジュールすることをお勧めします。

  172   assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
  173 

ヒープ(GCCauseオブジェクトが含まれている)を取得します。

  174   GenCollectedHeap* gch = GenCollectedHeap::heap();

フォアグラウンドの「若い」コレクションが必要かどうかを確認します

  175   if (_gc_count_before == gch->total_collections()) {
  176     // The "full" of do_full_collection call below "forces"
  177     // a collection; the second arg, 0, below ensures that
  178     // only the young gen is collected. XXX In the future,
  179     // we'll probably need to have something in this interface
  180     // to say do this only if we are sure we will not bail
  181     // out to a full collection in this attempt, but that's
  182     // for the future.

プログラムスレッドがヒープに干渉していませんか?

  183     assert(SafepointSynchronize::is_at_safepoint(),
  184       "We can only be executing this arm of if at a safepoint");

ガベージコレクションの原因(このコレクションの理由)をヒープから取得します。

  185     GCCauseSetter gccs(gch, _gc_cause);

若いスペースの完全なコレクションを行う

ヒープのmust_clear_all_soft_refsフラグの値を渡すことに注意してください。これは、OutOfMemoryシナリオではtrueに設定されている必要があり、いずれの場合も、「do_full_collection」にソフト参照に従わないように指示します。

  186     gch->do_full_collection(gch->must_clear_all_soft_refs(),
  187                             0 /* collect only youngest gen */);

_gc_causeは列挙型であり、(ここでは推測で)_allocation_failure回避の最初の試みで設定され、OutOfMemoryErrorその後_last_ditch_collectionは失敗します(一時的なガベージの収集を試みます)

メモリの「ヒープ」モジュールをざっと見てみると、do_full_collectionどの呼び出しでdo_collectionソフト参照が次の行で明示的に(「正しい」条件の下で)クリアされていることがわかります。

  480   ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());

---弱参照について学びたい人のために元の投稿が続きます---

マークアンドスイープアルゴリズムでは、ソフト参照はメインスレッドから追跡されません(したがって、別のブランチが非ソフト参照を介して到達できない限り、マークされません。

コピーアルゴリズムでは、オブジェクトのソフト参照が指しているものはコピーされません(別の非ソフト参照が到達しない限り)。

基本的に、実行の「メイン」スレッドから参照のWebをたどる場合、ソフト参照はたどられません。これにより、オブジェクトを指す参照がないかのように、オブジェクトをガベージコレクションできます。

ソフト参照が単独で使用されることはほとんどないことに注意することが重要です。これらは通常、デザインでオブジェクトへの複数の参照を持つオブジェクトで使用されますが、ガベージコレクションをトリガーするためにクリアする必要がある参照は1つだけです(コンテナーの保守を容易にするため、または高価な参照を検索する必要がない実行時のパフォーマンスのため) 。

于 2012-03-29T19:54:08.583 に答える
4

HotSpot FAQで、古くなっている可能性のある情報が1つ見つかりました: http ://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_softrefs

ソフト参照されたオブジェクトがいつフラッシュされるかを決定するものは何ですか?

1.3.1以降、ソフトに到達可能なオブジェクトは、最後に参照されてからしばらくの間、存続します。デフォルト値は、ヒープ内の空きメガバイトあたりのライフタイムの1秒です。この値は、ミリ秒を表す整数値を受け入れる-XX:SoftRefLRUPolicyMSPerMBフラグを使用して調整できます。たとえば、値を1秒から2.5秒に変更するには、次のフラグを使用します。

-XX:SoftRefLRUPolicyMSPerMB = 2500

Java HotSpot Server VMは、可能な最大ヒープサイズ(-Xmxオプションで設定)を使用して、残りの空き領域を計算します。

Java Hotspot Client VMは、現在のヒープサイズを使用して空き領域を計算します。

これは、サーバーVMがソフト参照をフラッシュするのではなくヒープを拡大する傾向があることを意味します。したがって、-Xmxは、ソフト参照がガベージコレクションされるタイミングに大きな影響を及ぼします。

一方、クライアントVMは、ヒープを増やすのではなく、ソフト参照をフラッシュする傾向が強くなります。

上記の動作は、JavaHotSpotVMの1.3.1からJavaSE6バージョンに当てはまります。ただし、この動作はVM仕様の一部ではなく、将来のリリースで変更される可能性があります。同様に、-XX:SoftRefLRUPolicyMSPerMBフラグは、特定のリリースに存在することが保証されていません。

バージョン1.3.1より前では、JavaHotSpotVMはソフト参照を検出するたびにそれらをクリアしていました。

さらに詳細は、http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html(MiserableVariableのコメント提供)で入手できます。

于 2012-04-03T21:59:19.380 に答える
3

答えが何であれ、特定の戦略に依存すると、すべてのJVM実装が異なる可能性があるため、ソフトウェアの信頼性が低下します。特定のJVMであっても、異なる構成を行うと、正確な戦略が変更され、ソフトウェアが破損する可能性があります。要約すると、特定の戦略に依存することは誤りです。

キャッシュはどのタイプのリソースを管理していますか?純粋なヒープ割り当てオブジェクトの場合、戦略は重要ではありません。ただし、ReferenceQueueを使用すると、SoftReferenceがクリアされたときに通知を受け取るのに役立つ場合があります。

リソースタイプがヒープに割り当てられたオブジェクトだけではない場合は、ユーザーに明示的なリリースメソッド(Closeable.close())を呼び出すように要求する必要があります。このリリースメソッドへの「忘れられた」呼び出しから保護するために、finalize()メソッドの実装を検討できますが、その副作用に注意してください。これについての詳細は、Joshua Blochの「EffectiveJava(2ndEdition)」の「Item7:ファイナライザーを避ける」を読むことをお勧めします。

于 2012-03-29T20:19:57.727 に答える
2

これが信頼できるというわけではありませんがSoftReference、怒りを込めて使用すると、VMのサイズを大きくする代わりにVMがそれらをフラッシュするのを見たことがありません。実はどういうわけかそうだと思い、デザインはそれに大きく依存していました。私は同じこと-msをし-mx ましたが、それは問題ではないはずです。

しかし、これが必要であると実際に言っている仕様は見つかりません。このブログでSoftReferencesは、フラッシュの方法について詳しく説明しているようです。簡単に読むと、他のメモリが使用可能であっても、それらをクリアできるようです。

于 2012-04-04T16:18:43.013 に答える
0

ブレーンストーミングだけ。キャッシュを他のキャッシュよりも先にクリアしたい場合は、2つをリンクできますか?おそらく、2番目のキャッシュのエントリへの強力な参照を保持し、自分のキャッシュのメンバーがクリアされたときにのみそれらの参照を解放することによってですか?

複雑なようです。私はおそらく、両方のキャッシュがまさにそれ、つまりキャッシュであることを単に受け入れることに傾倒するでしょう。キャッシュミスはパフォーマンスに悪影響を与える可能性がありますが、少なくともソフトウェアには複雑なキャッシュ管理戦略がありません。

于 2012-04-03T22:19:44.613 に答える