同じメモリ ( mmap
POSIX では Ignacio が言及しているようにMapViewOfFile
、Windows では) を複数の仮想アドレスにマッピングすると、興味深いコヒーレンシー パズルが得られる場合があります (あるアドレスでの書き込みは、別のアドレスでの読み取り時に表示されますか?)。またはそうでないかもしれません。すべてのプラットフォームの保証が何であるかはわかりません。
より一般的には、ポインターに数ビットを予約し、必要に応じてシフトします。
すべてのオブジェクトが 8 バイト境界に整列している場合、ポインタの最下位 3 ビットにタグを格納し、逆参照する前にそれらをマスクするのが一般的です (thkala が言及しているように)。16 バイトや 32 バイトなど、より高いアラインメントを選択すると、タグ付けに使用できる最下位ビットが 3 ビットまたは 5 ビットになります。同様に、タグ付けのためにいくつかの最上位ビットを選択し、逆参照する前にそれらをシフトオフします。(たとえば、IEEE-754 float (2 23値) または double (2 51値)のシグナリング NaN にポインターをパッキングする場合など、非連続ビットが使用されることがあります)。
ポインターのハイエンドを続けると、x86-64 の現在の実装では、64 ビット ポインター (0x0000000000000000-0x00007fffffffffff + 0xffff800000000000-0xffffffffffffffff) のうち最大で 48 ビットしか使用されず、Linux と Windows は最初の範囲のアドレスのみをユーザー空間に配布します。安全にマスクできる上位 17 ビットを残します。(ただし、これは移植性がなく、将来もそのままであることが保証されているわけではありません。)
もう 1 つのアプローチは、「ポインター」を考慮するのをやめて、JVM が-XX:+UseCompressedOops
. 512MB のプールを割り当て、8 バイトに整列されたオブジェクトを格納している場合、2 26個の可能なオブジェクトの場所があるため、32 の値にはインデックスに加えて 6 ビットの余裕があります。逆参照では、別の場所に保存されている配列のベース アドレスに、アラインメントを掛けたインデックスを追加する必要があります (すべての「ポインター」で同じです)。注意深く見ると、これは単に前の手法の一般化にすぎません (常にベースが 0 で、実際のポインターと整列します)。