Ruby如何区分VALUE与值和指针?

gre*_*man 3 ruby pointers

对于值,如true,nil或小的整数,红宝石做优化.它不是将VALUE指针用作指针,而是直接用于VALUE存储数据.

我想知道Ruby如何在这些用途之间产生影响:

def foo(x)
  ...
Run Code Online (Sandbox Code Playgroud)

x此相关联VALUE.从低级别来看,它们只是一个数字.如何判断某个数字是否是指向对象的指针?所有我想到的是限制指针将MSB设置为0,并将MSB的直接值设置为1.但这只是我的猜测.它是如何在Ruby中完成的?

Jör*_*tag 10

Ruby有许多不同的实现.Ruby语言规范没有规定对象的任何特定内部表示 - 为什么要这样做?毕竟,这是一个内部表征!

例如,JRuby根本不将对象表示为C指针,它将它们表示为Java对象.IronRuby将它们表示为.NET对象.Opal将它们表示为ECMAScript对象.MagLev将它们表示为Smalltalk对象.

但是,确实有一些实现使用您描述的策略.现在放弃的核磁共振成像就是这样做的,YARV和Rubinius也这样做了.

这实际上是一个非常古老的技巧,至少可以追溯到20世纪60年代.它被称为标记指针表示,并且如名称所示,您需要使用一些额外的元数据标记指针,以便知道它实际上是指向对象的指针还是某些其他数据类型的编码.

某些CPU具有专门用于此目的的特殊标记位.(例如,在AS/400上,CPU甚至没有指针,它有128位对象引用,即使原始CPU仅为48位宽,而较新的基于POWER的CPU为64位;使用额外位编码所有类型的元数据,如类型,所有者,访问限制等.)某些CPU具有用于其他目的的标记位,可以为此目的"滥用".但是,大多数现代主流CPU都没有标记位.

但是,你可以使用一招!在许多现代CPU上,未对齐的内存访问(访问不在字边界处开始的地址)实际上很慢(在某些情况下,甚至根本不可能),这意味着在32位CPU上,所有指针都是实际使用,以两位结束,00在64位CPU上以三位结束000.您可以使用这些位作为标记位:结尾指针00确实是指针,与结束指针01,10或者11是一些其他数据类型的编码.

在MRI中,结尾的指针1用于编码31/63位Fixnums.在YARV中,它们用于编码31/63位Fixnums,即根据公式(算术地说)或(作为位模式)编码为实际机器整数的整数.在64位平台上,YARV还使用指针结束,使用类似的方案对62位flonum进行编码.(如果你想知道为什么YARV中的a 是2n + 1,现在你知道:YARV使用内存地址作为对象ID,2n + 1是n的"内存地址".)2n+1(n << 1) | 110object_idFixnum

现在,怎么样nil,falsetrue?那么,在我们目前的计划中,它们没有空间.然而,非常低的存储器地址通常保留给操作系统内核,这意味着像一个指针024不能实际发生的程序.YARV使用该空间来编码nil,falsetrue:false被编码为0(这是方便的,因为这也是的编码false在C)中,nil被编码为0b1000true作为被编码0b10100(它使用的是0,0b100b100在引入flonums的前旧版本).

从理论上讲,还有很多空间来编码其他对象,但是YARV并没有这样做.例如,一些Smalltalk或Lisp VM在那里编码ASCII或BMP Unicode字符对象,或者一些常用的对象,如空列表,空数组或空字符串.

但是仍然有一些部分缺失:没有对象头,只有裸位模式,VM如何访问类,方法,实例变量等?好吧,它不能.这些必须是特殊的,并且硬编码到VM中.VM只需要知道结束的指针1是编码的Fixnum,并且必须知道该类是,Fixnum并且可以在那里找到方法.至于变量?好吧,你可以将它们与侧面字典中的对象分开存储.或者你走Ruby路线并完全不允许它们.