什么时候在 Ruby 中分配单例类?



在MRI Ruby中,何时分配单例类?

我刚刚发现单例类也可以有自己的单例类。

因此,可以调用

Object.singleton_class.singleton_class.singleton_class.singleton_class...

直到时间本身的尽头。显然,默认情况下,虚拟机不会分配所有这些单一实例类。

Ruby 虚拟机何时分配单一实例类?如果我不在其上定义任何方法并且不调用Object#singleton_class,则对象是否具有单例类?

从 Ruby 程序的角度来看,每个对象总是有一个单例类。 包括类。包括单例类。

但是,正如您所猜测的,某些 Ruby 实现通过不提前分配所有单例类来优化这一点。

以下是YARV所做的(据我所知(:

  1. 单例类是懒惰分配的。仅当您尝试访问或打开它,或者在其中定义方法、常量或变量时,才会创建它们。这是内存优化
  2. 但是,这种延迟分配有一些开销。由于模块和类通常具有单例方法,因此对于类和模块,这是不同的:对于类和模块,YARV 会在实例化(类/模块(对象后立即创建一个单例类。这是一种速度优化
  3. 当然,正如您在问题中所说,这会导致无限递归。因此,#2 仅适用于"普通"类,不适用于单例类。

所以:单例类是懒惰地实例化的。类和模块除外。异常中的例外是单例类。

但是,让我重复一下我在开头写的内容:这是 YARV 的私有内部实现细节。其他 Ruby 实现的行为可能相同,也可能不同。

Ruby中,每个对象始终存在一个单例类,包括单例类。

TL;DR根据我的实验,至少对于 YARV 2.6.5,所有单例类都是延迟分配的。

实验前

  • ObjectSpace.each_object(type)返回一个枚举器,该枚举器遍历所有.is_a?(type)的对象,只要它还没有被垃圾回收。

实验 1:定义空类

打开 irb,然后执行以下步骤。请注意,计算机上对象的绝对数量可能会有所不同,但差异很重要。

ObjectSpace.each_object(Class).count  #=> 649
class A; end
ObjectSpace.each_object(Class).count  #=> 650

定义空类仅分配 1 个类对象,因此不会分配单例类。

实验 2:定义仅包含实例方法的类

class B
def foo; end
def bar; end
end
ObjectSpace.each_object(Class).count  #=> 651

创建此类类也不会创建单例类。

实验 3:定义包含类方法的类

class C
def self.foo; end
end
ObjectSpace.each_object(Class).count  #=> 653

这次,创建了 2 个类,一个是类C本身,另一个是C的单例类。

实验4:新对象

a = A.new
ObjectSpace.each_object(Class).count  #=> 653

因此,实例化不会创建单例类。

实验 5:添加单例方法

def a.foo; end
ObjectSpace.each_object(Class).count  #=> 654

正如预期的那样,添加单一实例方法确实会创建单一实例类。

实验6:继承

class D; end
class E < D
def self.foo; end
end
ObjectSpace.each_object(Class).count  #=> 658

这次,分配了 4 个类,其中 2 个是"普通"类,另外 2 个是单例类。因为我们证明了创建空类不会动态分配单例类,所以在E中定义单例方法时必须分配D的单例类。因此,结论是,当必须创建单例类时,其父类(也是单例类(必须已经存在。如果没有,则会动态创建。

您可以自己进行模块的实验。

最新更新