Java VM はメモリ内のオブジェクトを移動しますか?移動する場合はどのように移動しますか?
-
01-07-2019 - |
質問
Java 仮想マシンはメモリ内のオブジェクトを移動することがありますか? 移動する場合、移動されたオブジェクトへの参照の更新はどのように処理されますか?
私が質問したのは、オブジェクトを分散形式で保存するというアイデアを模索しているからです。複数のサーバー間で)、効率上の理由からサーバー間でオブジェクトを移動する機能が必要です。オブジェクトには、リモート サーバー上のオブジェクトを含め、相互へのポインタを含めることができる必要があります。移動されたオブジェクトへの参照を更新する最良の方法を考えようとしています。
これまでの私のアイデアは次の 2 つです。
- オブジェクトの存続期間中は移動しないどこかに間接参照を維持し、オブジェクトが移動した場合は参照を更新します。しかし、これらの間接的な処理はどのように管理されるのでしょうか?
- 各オブジェクトの逆参照のリストを保持しておくと、オブジェクトが移動された場合に何を更新する必要があるかがわかります。もちろん、これによりパフォーマンスのオーバーヘッドが発生します。
これらのアプローチに関するフィードバックや、代替アプローチに関する提案に興味があります。
解決
ヒープのウォークに関する上記のコメントを参照してください。
GC が異なれば、その方法も異なります。
通常、コレクターはヒープ内を移動するときにコピーしますが、ヒープ内のすべてのオブジェクトを移動するわけではありません。むしろ、ヒープ内の LIVE オブジェクトを調べます。つまり、「ルート」オブジェクトからアクセスできる場合、そのオブジェクトはライブであるということになります。
そのため、この段階では、古いヒープから新しいヒープにオブジェクトをコピーするため、とにかくすべてのライブ オブジェクトにアクセスする必要があります。ライブ オブジェクトのコピーが完了すると、古いヒープに残るのは、すでにコピーされたオブジェクトかガベージのいずれかです。その時点で、古いヒープは完全に破棄できます。
この種のコレクターの 2 つの主な利点は、コピー フェーズ中にヒープを圧縮することと、生きているオブジェクトのみをコピーすることです。この種のコレクターを使用すると、オブジェクトの割り当てが非常に安価であり、文字通りヒープ ポインターをインクリメントするのとほとんど変わらないため、これは多くのシステムにとって重要です。GC が発生すると、「死んだ」オブジェクトはコピーされないため、コレクターの速度が低下することはありません。また、動的システムでは、長期にわたって存在するゴミよりも、少量の一時的なゴミの方がはるかに多いことがわかります。
また、ライブ オブジェクト グラフをたどることで、GC がどのようにすべてのオブジェクトを「認識」し、コピー中に実行されるアドレス調整の目的でオブジェクトを追跡できるかがわかります。
これは重要な問題であるため、GC の仕組みについて深く話すフォーラムではありませんが、これがコピー コレクターの動作の基本です。
世代コピー GC では、「古い」オブジェクトが異なるヒープに配置され、それらのオブジェクトは「新しい」ヒープよりも収集される頻度が低くなります。理論的には、長く存続するオブジェクトが古い世代に昇格し、収集される頻度が減り、全体的な GC パフォーマンスが向上するということです。
他のヒント
(実際には) ガベージ コレクション システムでは、オブジェクトをメモリ内で移動して、オブジェクトをより高密度に圧縮し、断片化の問題を回避する必要があります。
あなたが見ているのは、非常に大きく複雑な主題です。既存のリモート オブジェクト スタイル API について読んでおくことをお勧めします。.NET のリモート処理と、さらに遡ってのテクノロジ コルバ
参照を追跡するためのソリューションは、分散システムに存在するすべての障害モードに対処する必要があるため、複雑になります。JVM は、ネットワーク スイッチの不具合によりヒープの半分が突然見えなくなってしまうことを心配する必要はありません。
設計を掘り下げていくと、さまざまな失敗ケースをどのように処理するかが重要になると思います。
コメントへの返信:
あなたの質問は、分散形式でオブジェクトを保存することについて述べています。これは、.NET リモート処理と CORBA がまさに対処することです。確かに、どちらのテクノロジーもこれらのオブジェクトの移行をサポートしていません(私の知る限り)。しかし、どちらも分散オブジェクト システムの重要な部分であるオブジェクト ID の概念を広範囲に扱っています。システムのさまざまな部分は、どのオブジェクトについて話しているのかをどのようにして知るのでしょうか。
私は Java ガベージ コレクターの詳細についてはあまり詳しくありませんが、アプリケーションへの影響を最小限に抑えながら最大のパフォーマンスを達成するために、Java および .NET ガベージ コレクターには非常に複雑な機能があると確信しています。
ただし、ガベージ コレクションの基本的な考え方は次のとおりです。
- VM はすべてのスレッドのマネージド コードの実行を停止します。
- 既知の「ルート」のセットから到達可能性分析を実行します。静的変数、すべてのスレッド上のローカル変数。オブジェクトごとに、オブジェクト内のすべての参照をたどります。
- 到達可能性分析によって識別されないオブジェクトはすべてガベージです。
- まだ生きているオブジェクトをメモリ内で下に移動して、高密度に詰め込むことができます。これは、これらのオブジェクトへの参照も新しいアドレスで更新する必要があることを意味します。ガベージ コレクションがいつ発生するかを制御することにより、VM はオブジェクト参照が「空中に」存在しないことを保証できます。マシンレジスタに保持されているため)、問題が発生する可能性があります。
- プロセスが完了すると、VM はスレッドの実行を再開します。
このプロセスを改良したものとして、VM は世代別ガベージ コレクションを実行でき、オブジェクトの「年齢」に基づいて個別のヒープが維持されます。オブジェクトはヒープ 0 で開始され、数回の GC を乗り越えてもヒープ 1 に移行し、最終的にはヒープ 2 に移行します (ただし、.NET は 3 世代のみをサポートします)。この利点は、GC がヒープ 0 コレクションを非常に頻繁に実行できることであり、寿命の長いオブジェクト (最終的にヒープ 2 にある) がまだ生きている (ほぼ確実に生きている) ことを証明する作業を心配する必要がないことです。 。
同時ガベージ コレクションをサポートするための他の改良点や、GC がスケジュールされているときに実際にアンマネージ コードを実行するスレッドに関する詳細があり、この領域がさらに複雑になります。
ご要望について詳しく知りたいです。別の回答が示すように、テラコッタはまさにあなたが探しているものかもしれません。
ただし、Terracotta が提供するものと、あなたが求めているものの間には微妙な違いがあるため、私の質問です。
違いは、あなたに関する限り、Terracotta はオブジェクトへの「リモート」参照を提供しないことです。実際、RMI、JMS などの「リモート」概念全体が提供されます。テラコッタを使用する場合はまったく存在しません。
むしろ、Terracotta では、すべてのオブジェクトは大きな仮想ヒープに存在します。ノード 1、ノード 2、ノード 3、ノード 4 などのスレッドはすべて、仮想ヒープ内の任意のオブジェクトにアクセスできます。
特別なプログラミングを学習したり、特別な API を使用したりする必要はなく、「仮想」ヒープ内のオブジェクトはローカル ヒープ内のオブジェクトとまったく同じ動作をします。
つまり、Terracotta が提供するのは、単一の JVM のプログラミング モデルとまったく同じように動作する、複数の JVM のプログラミング モデルです。別々のノード内のスレッドは、単一ノード内のスレッドと同様に動作します。オブジェクトの変更、同期、待機、通知はすべて、ノード間でもスレッド間でもまったく同じように動作します。違いはありません。
さらに、これまでのソリューションとは異なり、オブジェクト参照はノード間で維持されます。つまり、== を使用できます。これはすべて、クラスタ全体で Java メモリ モデルを維持する一環であり、「通常の」Java (例:POJO、同期、待機/通知) は機能します (クラスター全体でオブジェクト ID を保持しない/保持できない場合は、どれも機能しません)。
そこで、要件をさらに絞り込むための質問が戻ってきます。「リモート」ポインタはどのような目的で必要ですか?
あなたが求めているキーワードは「ガベージ コレクターの圧縮」です。JVM はこれを使用することが許可されており、オブジェクトを再配置できることを意味します。JVM のマニュアルを参照して、JVM がサポートしているかどうか、また JVM に影響を与えるコマンドライン オプションがあるかどうかを確認してください。
コンパクションを説明する概念的に最も簡単な方法は、ガベージ コレクターがすべてのスレッドをフリーズし、オブジェクトを再配置し、ヒープとスタックでそのオブジェクトへのすべての参照を検索し、それらを新しいアドレスで更新すると仮定することです。実際には、これはもっと複雑です。パフォーマンス上の理由から、スレッドが停止した状態で完全なスイープを実行することは望ましくありません。そのため、インクリメンタル ガベージ コレクターは、可能な限り圧縮の準備として作業を実行します。
間接参照に興味がある場合は、Java の弱い参照とソフト参照、およびさまざまな RPC システムで使用されるリモート参照を調べることから始めることができます。
テラコッタやオラクルの Java オブジェクト キャッシュ (以前のタンガーソル) のような分散キャッシュを探しているようですね。
さらに深く掘り下げたい場合は、JBoss Cache アーキテクチャのドキュメントを参照し、そのソースコードの一部を参考として入手してください。
これはあなたが説明したものとまったく同じではありませんが、非常によく似た動作をします。
ここにリンクがあります。
http://www.jboss.org/jbosscache/
これがお役に立てば幸いです。