为什么冻结哈希文字与冻结字符串文字不同?

  • 本文关键字:冻结 文字 字符串 哈希 ruby
  • 更新时间 :
  • 英文 :


我一直在阅读有关减少Ruby/Rails应用程序中内存使用的方法,其中提到的一件事是冻结对象。我已经尝试了下面的代码(MRI,Ruby 2.3.3(,根据活动监视器,与不冻结字符串相比,它确实节省了内存。

pipeline = []
100_000.times { pipeline << 'hello world'.freeze }

但是,如果我尝试使用哈希文字进行相同的操作,它会占用大量内存,除非我将哈希分配给变量并冻结它。

pipeline = []
100_000.times { pipeline << {hello: 'world'}.freeze } # Uses about 25MB
my_hash = {hello: 'world'}
my_hash.freeze
100_000.times { pipeline <<  my_hash} # This uses about 1MB

谁能解释为什么?我一直认为字符串大小写有点奇怪,因为它看起来你只是在创建许多不同的字符串对象,分别冻结每个对象,并向数组中添加大量冻结对象。不知道它为什么有效,但嘿,它确实如此。现在,哈希大小写更符合我的预期,但我不知道为什么它的行为不像字符串。

Ruby 优化器可能可以将该字符串从一个循环识别为相同,但它无法将该哈希识别为相同,因此它会生成新的哈希。在第二个变体中,您实际上使用相同的哈希,以便优化器可以处理它。

为了证明这一点,请看这个:

pipeline = []
100_000.times { pipeline << 'hello world'.freeze }
pipeline.map(&:object_id).uniq.length
# => 1

这是一个相同对象的数组,只有一个分配。

pipeline = []
100_000.times { pipeline << {hello: 'world'}.freeze }     
pipeline.map(&:object_id).uniq.length
# => 100000 

这是 100,000 个不同的对象。

谁能解释为什么?我一直认为字符串大小写有点奇怪,因为它看起来你只是在创建许多不同的字符串对象,分别冻结每个对象,并向数组中添加大量冻结对象。

表达式形式

'string literal'.freeze

是语言特殊情况的特殊表达形式。它不仅冻结字符串对象,还执行重复数据消除。(类似于符号。

它是一种特殊大小写的表达式形式。它不是计算字符串文字,然后将消息发送到freeze。相反,它被视为单个实体,如果您愿意,可以将其视为不同形式的字符串文本。

事实上,最初的提案确实引入了一种不同形式的字符串文字,如下所示:

'string literal'f

该提案被更改以使其向前兼容:如果您必须在旧版本的 Ruby 中运行代码,而'foo'.freeze在旧版本的 Ruby 中的工作方式相同,'foo'f将是一个语法错误,它只使用更多的内存。

注意:这意味着它仅适用于文字。在这里,字符串被消除重复:

'foo'.freeze

在这里,它不是:

foo = 'foo'
foo.freeze

不知道它为什么有效,但嘿,它确实如此。

基本上,它是有效的,因为语言规范是这样说的。

现在,哈希大小写更符合我的预期,但我不知道为什么它的行为不像字符串。

同样,它不起作用,因为语言规范仅处理特殊情况的字符串文字。

最新更新