Ruby中的Fixnum object_ids上的模式?

Hom*_*ith 2 ruby objectid

object_id为0为1,1为3,2为5.

为什么这种模式是这样的?Fixnums背后的内容是什么来创建object_ids模式?我希望如果0有id 1,1有id 2,2有id 3 ..依此类推.

我错过了什么?

Jör*_*tag 6

首先:在只有大约Ruby语言规范保证的事情object_ids是,他们是唯一在太空中.而已.它们在时间上甚至都不是唯一的.

因此,在任何给定的时间只能有一个特定的一个对象object_id 在同一时间,然而,在不同的时间,object_id之间可以重复用于不同的对象.

完全准确:Ruby保证的是什么

  • object_id 将是一个 Integer
  • 没有两个对象同时object_id具有相同的功能
  • 一个对象object_id在其整个生命周期中都具有相同的功能

您所看到的是YARV 中如何实施object_idFixnum实施的副作用.这是YARV的私有内部实现细节,不以任何方式保证.其他Ruby实现可能(并且确实)以不同方式实现它们,因此在Ruby实现中不能保证这一点.在不同版本的YARV中,甚至在不同平台上的相同版本,甚至都不能保证这一点.

而事实上,它实际上没有改变最近,它 32位和64位平台之间的不同.

在YARV中,object_id简单地实现为返回对象的内存地址.这是一个难题.

Nut,为什么Fixnums 的内存地址如此规律?嗯,实际上,在这种情况下,它们不是内存地址!YARV使用特殊技巧将一些对象编码为指针.有一些指针实际上没有被使用,所以你可以使用它们来编码某些东西.

这被称为标记指针表示,并且是几十年来在许多不同的解释器,VM和运行时系统中使用的非常常见的优化技巧.几乎每个Lisp实现都使用它们,许多Smalltalk VM,许多Ruby解释器等等.

通常,在这些语言中,您总是传递指向对象的指针.对象本身由对象头,它包含对象元数据(如一个对象的类型,它的类(ES),也许访问控制限制或安全注解等),然后将实际的对象数据本身的.因此,一个简单的整数将表示为指针加上一个由元数据和实际整数组成的对象.即使有一个非常紧凑的表示,对于一个简单的整数,这就像6字节.

此外,您无法将此类整数对象传递给CPU以执行快速整数运算.如果要添加两个整数,实际上只有两个指针,指向要添加的两个整数对象的对象标题的开头.因此,首先需要对第一个指针执行整数运算,以将偏移量添加到存储整数数据的对象中.然后你必须取消引用该地址.使用第二个整数再次执行相同操作.现在你有两个整数,你可以实际要求CPU添加.当然,您现在需要构造一个新的整数对象来保存结果.

因此,为了执行一个整数加法,您实际上需要执行三个整数加法加上两个指针dererefences加上一个对象构造.你占用了近20个字节.

但是,诀窍是,所谓的不可变的值类型喜欢整数,你通常不会需要的所有元数据对象中的标头:你可以把所有的东西出来,简单地合成它(这是VM-nerd-当有人关心的时候说"假它".fixnum将始终具有类Fixnum,不需要单独存储该信息.如果有人使用反射来找出fixnum的类,你只需回复Fixnum,没有人会知道你实际上并没有将这些信息存储在对象头中,事实上,甚至没有一个对象头(或者宾语).

因此,关键是要存储值指针内的对象对象,有效地坍塌两成一个.

有些CPU实际上在指针内有额外的空间(所谓的标记位),允许您在指针本身内存储有关指针的额外信息.额外的信息,如"这实际上不是一个指针,这是一个整数".例子包括Burroughs B5000,各种Lisp机器或AS/400.不幸的是,目前大多数主流CPU都没有这个功能.

但是,有一条出路:当地址未在字边界上对齐时,大多数当前的主流CPU工作速度明显变慢.有些甚至根本不支持未对齐访问.

这意味着在实践中,所有指针都可以被4整除(在32位系统上,在64位系统上为8),这意味着它们总是以两个(64位系统上的三个)0位结束.这允许我们区分真实的指针(结尾00)和伪装的实际整数的指针(结尾的指针1).它仍然留给我们所有指针,以10自由结束做其他的东西.此外,大多数现代操作系统为自己保留了非常低的地址,这为我们提供了另一个混乱的区域(以24 0秒开头并结束的指针00).

因此,您可以将31位(或63位)整数编码为指针,只需将其向左移1位并添加1即可.你可以通过简单地移动它们来执行非常快速的整数运算(有时甚至不需要).

我们如何处理其他地址空间?那么,典型的例子包括编码float在其他大的地址空间S和数量之类的特殊对象true,false,nil,127个ASCII字符,一些常用的短字符串,空列表,空的对象,空数组等附近的0地址.

在YARV,整数被编码的I上述方式,false被编码为地址0(这恰好成为的表示false在C)中,true作为地址2(这恰好是的C-表示true移位一位)和nil作为4.

YARV中,以下位模式用于编码某些特殊对象:

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
Run Code Online (Sandbox Code Playgroud)

Fixnums是63位整数,适合单个机器字,flonums是62位Float,适合单个机器字.false,nil并且true是您所期望的,undefined是一个仅在实现中使用但不向程序员公开的值.

请注意,在32位平台上,flonum不使用s(使用30位Floats 没有意义),因此位模式不同.nil.object_id例如,4在32位平台上,8与64位平台不同.

所以你有它:

  • 某些小整数被编码为指针
  • 指针用于object_ids

因此

  • 某些小整数具有可预测object_id小号