【问题标题】:Why freezing hash literal is not the same as freezing string literal?为什么冻结哈希文字与冻结字符串文字不同?
【发布时间】:2017-07-28 01:15:36
【问题描述】:

我一直在阅读有关在我的 Ruby/Rails 应用程序中减少内存使用的方法,其中一件事是 mentioned 是冻结对象。我已经尝试了下面的代码(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


    【解决方案1】:

    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 个不同的对象。

    【讨论】:

    • 谢谢!我尝试了你的第一个例子,但我得到的长度是 100000。无论如何,我想我知道你的意思。我检查了数组中的对象 ID,发现里面的对象是相同的。我现在发现了一些关于 Ruby 2.1 的东西,似乎他们当时确实对解释器进行了一些更改,以允许字符串像你说的那样表现。
    • Ruby 的每个重要版本都增加了更多优化。尝试使用 2.4,您可能会得到更接近我在这里得到的结果。
    • 我认为您需要pipeline.map(&amp;:object_id).uniq.length 进行此测试,不是吗?无论如何,我在 2.2.5 和 2.4.1 中的这些测试中分别得到 1 和 100000
    • @DavidAldridge 你是对的。那是在我的测试代码中,但由于某种原因没有放在这里。我已经更新了答案。
    【解决方案2】:

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

    表达形式

    'string literal'.freeze
    

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

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

    其实原提案确实引入了不同形式的字符串字面量,像这样:

    'string literal'f
    

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

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

    'foo'.freeze
    

    这里不是:

    foo = 'foo'
    foo.freeze
    

    不知道为什么它会起作用,但是,它确实起作用了。

    基本上,它可以工作,因为语言规范是这样说的。

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

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

    【讨论】:

      猜你喜欢
      • 2012-10-14
      • 2012-06-20
      • 1970-01-01
      • 2013-10-04
      • 2011-04-02
      • 2016-11-14
      • 1970-01-01
      • 2022-06-22
      • 1970-01-01
      相关资源
      最近更新 更多