4

X86-64、Linux、Windows。

考虑一下我想做某种“标签指针的免费启动”。基本上我希望有两个指针指向同一个实际内存块,但它们的位不同。(例如,我希望 GC 收集或出于其他原因使用一位)。

intptr_t ptr = malloc() 
intptr_t ptr2 = map(ptr | GC_FLAG_REACHABLE) //some magic call

int* p = int*(ptr);
int* p2 = int*(ptr2);
*p = 10;
*p2 = 20;
assert(*p == 20)
assert(p != p2)
4

3 回答 3

2

将相同的内存(mmap在 Ignacio 提到的 POSIX 上,MapViewOfFile在 Windows 上)映射到多个虚拟地址可能会为您提供一些有趣的一致性难题(在另一个地址读取时,在一个地址上的写入是否可见?)。或者可能不是。我不确定所有平台保证是什么。

更常见的是,只需在指针中保留几位,并根据需要进行移位。

如果您的所有对象都与 8 字节边界对齐,则通常只需将标签存储在指针的 3 个最低有效位中,并在取消引用之前将它们屏蔽掉(如 thkala 所述)。如果选择更高的对齐方式,例如 16 字节或 32 字节,则有 3 或 5 个最低有效位可用于标记。等效地,选择一些最重要的位进行标记,并在取消引用之前将它们移开。(有时会使用非连续位,例如,当将指针打包到 IEEE-754 浮点数(2 23个值)或双精度数(2 51 个值)的信令 NaN 中时。)

继续指针的高端,x86-64 的当前实现最多使用 64 位指针中的 48 位(0x0000000000000000-0x00007fffffffffff + 0xffff800000000000-0xffffffffffffffff),Linux 和 Windows 仅将第一个范围内的地址分发给用户空间,留下 17 个最重要的位可以安全地屏蔽掉。(不过,这既不是可移植的,也不能保证在未来保持真实。)

另一种方法是停止考虑“指针”,而只是将索引用于更大的内存数组,就像 JVM 对-XX:+UseCompressedOops. 如果您分配了一个 512MB 的池并存储 8 字节对齐的对象,则有 2 26个可能的对象位置,因此除了索引之外,32 值还有 6 位可用。取消引用将需要将索引乘以对齐到数组的基地址,并保存在其他位置(每个“指针”都相同)。如果您仔细观察,这只是对先前技术的概括(其基数始终为 0,事物与实际指针对齐)。

于 2012-06-03T21:32:39.977 回答
2

在 Linux 上, mmap() 同一个文件两次。在 Windows 上也是如此,但它有自己的一套功能。

于 2012-06-02T06:23:43.823 回答
1

曾几何时,我从事 Prolog 实现,该实现使用以下技术在指针中包含备用位:

  • 分配具有已知对齐方式的内存区域。malloc()通常以 4 字节或 8 字节对齐方式分配内存。如有必要,用于posix_memalign()获取具有更高对齐大小的区域。

  • 由于生成的指针与多个字节的间隔对齐,但它表示字节精确的地址,因此您有一些备用位,根据定义,在内存区域指针中将为零。例如,4 字节对齐在指针的 LSB 侧为您提供了两个备用位。

  • 你用这些位或(|)你的标志,现在有一个标记的指针。

  • 只要您在将指针用于内存访问之前注意正确屏蔽指针,就应该完全没问题。

于 2012-06-03T18:29:45.493 回答