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

时间:2017-07-28 01:15:37

标签: ruby

我一直在阅读有关减少Ruby / Rails应用程序内存使用量的方法,而mentioned就是冻结对象。我已经尝试了下面的代码(MRI,Ruby 2.3.3)并且它确实可以节省内存,根据Activity Monitor,与不冻结字符串相比。

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

任何人都可以解释原因吗?我一直认为字符串的情况有点奇怪,因为看起来你只是简单地创建了许多不同的字符串对象,分别冻结每个字符串对象,并向数组中添加了大量的冻结对象。不知道它为什么会起作用,但是嘿,确实如此。现在,散列情况更符合我的预期,但我不知道为什么它不会像字符串那样行事。

2 个答案:

答案 0 :(得分:2)

可能的情况是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个不同的对象。

答案 1 :(得分:2)

  

任何人都可以解释原因吗?我一直认为字符串的情况有点奇怪,因为看起来你只是创建了许多不同的字符串对象,分别冻结每个字符串对象,并向数组中添加了大量的冻结对象。

表达形式

'string literal'.freeze

是一种特殊的表达形式,由该语言特殊处理。它不仅冻结了字符串对象,还执行重复数据删除。 (与符号类似。)

这是一种特殊的表达形式。 评估字符串文字,然后向其发送消息freeze。相反,它被视为单个实体,如果你愿意,它将被视为一种不同形式的字符串文字。

事实上,原始提案 引入了不同形式的字符串文字,如下所示:

'string literal'f

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

注意:这意味着适用于文字。这里,字符串是重复数据删除:

'foo'.freeze

这里不是:

foo = 'foo'
foo.freeze
  

不知道它为什么会起作用,但嘿,确实如此。

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

  

现在,散列情况更符合我的预期,但我不知道为什么它不会像字符串一样。

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