我一直在阅读有关减少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
任何人都可以解释原因吗?我一直认为字符串的情况有点奇怪,因为看起来你只是简单地创建了许多不同的字符串对象,分别冻结每个字符串对象,并向数组中添加了大量的冻结对象。不知道它为什么会起作用,但是嘿,确实如此。现在,散列情况更符合我的预期,但我不知道为什么它不会像字符串那样行事。
答案 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
不知道它为什么会起作用,但嘿,确实如此。
基本上,它有效,因为语言规范是这样说的。
现在,散列情况更符合我的预期,但我不知道为什么它不会像字符串一样。
同样,它不起作用,因为语言规范只有特殊情况的字符串文字。