1

私の目標は、Javaで割り当てられた配列が、隣接する物理メモリ全体に確実に割り当てられるようにすることです。私が遭遇した問題は、本当に大きな配列を割り当てない限り、配列が割り当てられているページが物理メモリ内で連続していない傾向があることです。

私の質問は次のとおりです。

  • 本当に大きな配列が物理メモリ内で隣接するページを保証するのはなぜですか?
  • アレイを物理メモリ全体に確実に割り当てる方法はありますか?それは、アレイを実際に大きくすることを含みませんか?
  • キャッシュヒット/キャッシュミスを測定せずに、Javaオブジェクト/配列が存在するページまたは物理アドレスを確認するにはどうすればよいですか?

なぜJavaでこれを行っているのかを尋ねる答えを探していません。私は、Cが「私の問題を解決する」こと、そしてJavaの基本的な性質に反することを理解しています。それにもかかわらず、私にはこれを行う正当な理由があります。

答えが常に機能することを保証する必要はありません。私はほとんどの場合うまくいく答えを探しています。合理的なJavaプログラマーが書くことのない、独創的ですぐに使える答えのための追加のポイント。プラットフォーム固有(x86 32ビット64ビット)でも問題ありません。

4

6 に答える 6

6

いいえ。物理的に隣接するメモリには、OSとの直接の対話が必要です。ほとんどのアプリケーション(JVMを含む)は、事実上連続したアドレスのみを取得します。また、JVMは、OSから取得できないものを提供することはできません。

その上、なぜあなたはそれが欲しいのですか?DMA転送を設定している場合は、とにかくJava以外の手法を使用している可能性があります。

背景のビット:

最新のPCの物理メモリは、通常、交換可能なDIMMモジュールでは柔軟な量です。各バイトには物理アドレスがあるため、起動時にオペレーティングシステムが使用可能な物理アドレスを決定します。これらのアドレスを直接使用しない方が、アプリケーションの方が優れていることがわかります。代わりに、すべての最新のCPU(およびそれらのキャッシュ)は仮想アドレスを使用します。物理アドレスへのマッピングテーブルがありますが、これは完全である必要はありません。ディスクへのスワップは、物理アドレスにマッピングされていない仮想アドレスを使用することで有効になります。マッピングが不完全なプロセスごとに1つのテーブルを使用することで、別のレベルの柔軟性が得られます。プロセスAに物理アドレスXにマップする仮想アドレスがあるが、プロセスBにはない場合、プロセスBが物理アドレスXに書き込む方法はなく、そのメモリはプロセスA専用であると見なすことができます。

マッピングテーブルはページレベルで機能します。ページ、または物理アドレスの連続したサブセットは、仮想アドレスの連続したサブセットにマップされます。オーバーヘッドと粒度のトレードオフにより、4KBページが一般的なページサイズになりました。ただし、各ページには独自のマッピングがあるため、そのページサイズを超える隣接性を想定することはできません。特に、ページが物理メモリから削除され、ディスクにスワップされ、復元されると、新しい物理メモリアドレスに到達する可能性が非常に高くなります。仮想アドレスは変更されないため、プログラムは認識しません。OSが管理するマッピングテーブルのみが変更されます。

于 2009-05-06T15:15:49.620 に答える
5

ガベージコレクターが(論理)メモリ内でオブジェクトを移動することを考えると、運が悪いと思います。

最善の方法は、ByteBuffer.allocateDirectを使用することです。これは(通常)GCによって(論理)メモリ内を移動することはありませんが、物理メモリ内で移動されたり、ディスクにページアウトされたりする場合があります。より良い保証が必要な場合は、OSをヒットする必要があります。

そうは言っても、ページサイズをヒープと同じ大きさに設定できる場合、すべての配列は必然的に物理的に隣接します(またはスワップアウトされます)。

于 2009-05-06T15:35:38.417 に答える
2

sun.java.unsafeを使用したいと思います。

于 2009-05-06T14:59:27.570 に答える
2

特定のJVMをだましてやりたいことを実行させる方法はあるかもしれませんが、これらはおそらく壊れやすく、複雑で、JVM、そのバージョン、実行するOSなどに非常に固有である可能性があります。

ですから、あなたの問題についてもっと知らなければ、誰も助けることができないと思います。確かに、Javaで一般的に、せいぜい特定のJVMでそれを行う方法はありません。

代替案を提案するには:

本当に連続したメモリにデータを格納する必要がある場合は、小さなCライブラリに格納して、JNI経由で呼び出してみませんか?

于 2009-05-06T15:05:27.290 に答える
2

私の考えでは。あなたはまだ理由を説明していません

  • そのプリミティブ配列はメモリ内で連続していません。それらが仮想メモリで連続しない理由がわかりません。(cfオブジェクトの配列のオブジェクトがメモリ内で連続している可能性は低いです)
  • 物理メモリ(RAM、つまりランダムアクセスメモリ)で連続していないアレイでは、パフォーマンスに大きな違いがあります。たとえば、アプリケーションのパフォーマンスの測定可能な違い。

Cでこれを行うことに慣れているため、配列を割り当てるための低レベルの方法を実際に探しているように見えます。パフォーマンスは、これを行う必要性の主張です。

ところで:たとえばgetDouble()/ putDouble()を使用してByteBuffer.allocateDirect()にアクセスすると、double []を使用するよりも遅くなる可能性があります。前者はJNI呼び出しを含み、後者は呼び出しがまったくないように最適化できるためです。

これを使用する理由は、JavaスペースとCスペースの間でデータを交換するためです。例:NIOコール。読み取り/書き込みが最小限に抑えられている場合にのみ、良好に機能します。それ以外の場合は、Javaスペースで何かを使用することをお勧めします。

つまり、自分が何をしているのか、なぜそれをしているのかが明確でない限り、気分が良くなる可能性のある解決策になってしまう可能性がありますが、実際には単純な解決策よりも複雑でパフォーマンスが低下します。

于 2009-05-08T05:51:14.267 に答える
0

System.identityHashCode()とオブジェクトのメモリアドレスの識別について説明している関連する質問に対するこの回答に注意してください。つまり、デフォルトの配列hashCode()実装を使用して、配列の元のメモリアドレスを識別できます(int / 32ビットに適合させる必要があります)。

于 2009-05-06T15:05:14.903 に答える