Ruby中Fixnum对象\u ID上的模式?
0的对象id为1,1的对象id为3,2的对象id为5 为什么这个图案是这样的?创建对象ID模式的修复程序背后是什么?我希望如果0具有id 1,1具有id 2,2具有id 3。。等等Ruby中Fixnum对象\u ID上的模式?,ruby,objectid,Ruby,Objectid,0的对象id为1,1的对象id为3,2的对象id为5 为什么这个图案是这样的?创建对象ID模式的修复程序背后是什么?我希望如果0具有id 1,1具有id 2,2具有id 3。。等等 我缺少什么?对于FixnumI,对象id是I*2+1 对于0,2,4的对象id,它们是什么?在ruby中,它们是false,true,nil。第一件事:ruby语言规范对object\u id的唯一保证是它们在空间上是唯一的。就这样。它们甚至在时间上都不是独一无二的 因此,在任何给定时间,同一时间只能有一个对象具有特
我缺少什么?对于Fixnum
I
,对象id是I*2+1
对于0,2,4
的对象id,它们是什么?在ruby中,它们是false
,true
,nil
。第一件事:ruby语言规范对object\u id
的唯一保证是它们在空间上是唯一的。就这样。它们甚至在时间上都不是独一无二的
因此,在任何给定时间,同一时间只能有一个对象具有特定的object\u id
,但是,在不同的时间,object\u id
s可以对不同的对象重复使用
确切地说,Ruby保证的是
将是一个object\u id
整数
- 没有两个对象将同时具有相同的
对象id
- 对象在其整个生命周期中将具有相同的
对象id
object\u id
和Fixnum
s的副作用。这是YARV的一个私有内部实现细节,没有任何保证。其他Ruby实现可能(并且确实)以不同的方式实现它们,因此不能保证在Ruby实现中都是如此。它甚至不能保证在不同版本的YARV中是正确的,甚至不能保证在不同平台上的同一版本中也是正确的
事实上,它最近确实发生了变化,并且在32位和64位平台之间有所不同
在YARV中,object\u id
简单地实现为返回对象的内存地址。这是谜题的一部分
Nut,为什么Fixnum
s的内存地址如此规则?实际上,在这种情况下,它们不是内存地址!YARV使用一种特殊的技巧将一些对象编码为指针。有一些指针实际上没有被使用,所以你可以用它们来编码某些东西
这被称为标记指针表示,是几十年来在许多不同的解释器、VM和运行时系统中使用的一种非常常见的优化技巧。几乎每个Lisp实现都使用它们,许多Smalltalk虚拟机,许多Ruby解释器,等等
通常,在这些语言中,您总是传递指向对象的指针。对象本身包括一个对象头,其中包含对象元数据(如对象的类型、类、可能的访问控制限制或安全注释等),然后是实际的对象数据本身。因此,一个简单的整数将表示为指针加上一个由元数据和实际整数组成的对象。即使是一个非常紧凑的表示,对于一个简单的整数来说,也有点像6字节
此外,您不能将这样的整数对象传递给CPU以执行快速整数运算。如果要添加两个整数,实际上只有两个指针,它们指向要添加的两个整数对象的对象头的开头。因此,首先需要对第一个指针执行整数运算,将偏移量添加到存储整数数据的对象中。然后你必须取消对那个地址的引用。对第二个整数再次执行相同的操作。现在你有两个整数,你可以要求CPU加上。当然,现在需要构造一个新的整数对象来保存结果
因此,为了执行一个整数加法,实际上需要执行三个整数加法加上两个指针引用加上一个对象构造。您占用了将近20个字节
然而,诀窍在于,对于所谓的不可变值类型(如整数),您通常不需要对象头中的所有元数据:您可以忽略所有这些内容,只需合成它(这是VM nerd的“fake it”之意),只要有人愿意看。fixnum将始终具有类fixnum
,不需要单独存储该信息。如果有人使用反射来确定fixnum的类,您只需回答fixnum
,没有人会知道您实际上没有将该信息存储在对象头中,事实上,甚至没有对象头(或对象)
所以,诀窍是将对象的值存储在指向对象的指针中,有效地将二者合并为一
有些CPU实际上在指针中有额外的空间(所谓的标记位),允许您在指针本身中存储有关指针的额外信息。额外信息,如“这实际上不是一个指针,这是一个整数”。示例包括Burroughs B5000、各种Lisp机器或AS/400。不幸的是,目前大多数主流CPU都没有这个功能
然而,有一个解决办法:当地址不在字边界上对齐时,大多数当前主流CPU的工作速度明显较慢。有些甚至根本不支持未对齐的访问
这意味着在实践中,所有指针都可以被4整除(在32位系统上是8,在64位系统上是8),这意味着它们总是以2(在64位系统上是3)0
位结束。这使我们能够区分实指针(以00
结尾)和实际上是伪装的整数的指针(以1
结尾的指针)。它仍然给我们留下了所有以10
结尾的指针,让我们可以自由地做其他事情。此外,大多数现代操作系统都为自己保留非常低的地址,这给了我们另一个麻烦(指针以240
s开头,以00
结尾)
因此,您可以对31位(或63位)进行编码
xxxx xxxx … xxxx xxx1 Fixnum
xxxx xxxx … xxxx xx10 flonum
0000 0000 … 0000 1100 Symbol
0000 0000 … 0000 0000 false
0000 0000 … 0000 1000 nil
0000 0000 … 0001 0100 true
0000 0000 … 0011 0100 undefined