为什么在ruby中动态创建大量符号不是一个好主意?

时间:2011-01-01 12:05:42

标签: ruby string symbols

红宝石中符号的功能是什么?字符串和符号之间有什么区别? 为什么动态创建大量符号不是一个好主意?

5 个答案:

答案 0 :(得分:87)

符号就像字符串,但它们是不可变的 - 它们不能被修改。

它们只被放入内存一次,使得它们非常有效地用于哈希中的键,但它们会一直存在,直到程序退出。如果你误用它们,这会使它们成为一种记忆力。

如果动态创建大量符号,则会分配大量内存,直到程序结束才能释放。如果你知道的话,你应该只动态创建符号(使用string.to_sym):

  1. 需要重复访问符号
  2. 无需修改
  3. 正如我之前所说,它们对哈希这样的东西很有用 - 你更关心变量的身份而不是它的值。正确使用符号时,符号是一种可读且有效的传递身份的方式。

    我将解释我对你的评论符号的不变性的意思。

    字符串就像数组;它们可以在适当的位置进行修改:

    12:17:44 ~$ irb
    irb(main):001:0> string = "Hello World!"
    => "Hello World!"
    irb(main):002:0> string[5] = 'z'
    => "z"
    irb(main):003:0> string
    => "HellozWorld!"
    irb(main):004:0> 
    

    符号更像数字;它们无法编辑到位:

    irb(main):011:0> symbol = :Hello_World
    => :Hello_World
    irb(main):012:0> symbol[5] = 'z'
    NoMethodError: undefined method `[]=' for :Hello_World:Symbol
        from (irb):12
        from :0
    

答案 1 :(得分:9)

无论在何处使用,符号都是相同的对象和相同的内存分配:

>> :hello.object_id
=> 331068
>> a = :hello
=> :hello
>> a.object_id
=> 331068
>> b = :hello
=> :hello
>> b.object_id
=> 331068
>> a = "hello"
=> "hello"
>> a.object_id
=> 2149256980
>> b = "hello"
=> "hello"
>> b.object_id
=> 2149235120
>> b = "hell" + "o"

两个“相同”的字符串,因为它们包含相同的字符,可能不会引用相同的内存,如果您使用字符串,例如哈希,这可能效率低下。

因此,符号可用于减少内存开销。但是 - 它们是等待发生的内存泄漏,因为一旦创建符号就不能进行垃圾回收。创建成千上万的符号将分配内存而不是可恢复的。糟糕!

答案 2 :(得分:6)

从用户输入创建符号而不验证某种白名单的输入(例如,RoR中的查询字符串参数)可能特别糟糕。如果用户输入转换为符号而未经验证,则恶意用户可能会导致程序消耗大量内存,而这些内存永远不会被垃圾回收。

错误(无论用户输入如何都会创建符号):

name = params[:name].to_sym

好(只有在允许用户输入的情况下才会创建符号):

whitelist = ['allowed_value', 'another_allowed_value']
raise ArgumentError unless whitelist.include?(params[:name])
name = params[:name].to_sym

答案 3 :(得分:2)

如果您使用的是Ruby 2.2.0或更高版本,通常可以动态创建大量符号,因为它们将根据Ruby 2.2.0-preview1 announcement进行垃圾回收,其中包含{{3}的链接}}。但是,如果将动态符号传递给某种将其转换为ID的代码(C源代码中使用的内部Ruby实现概念),那么在这种情况下它将被固定并且永远不会被垃圾收集。我不确定这种情况有多常见。

您可以将符号视为某事物的名称,将字符串(粗略地)视为一系列字符。在许多情况下,您可以使用符号或字符串,也可以使用两者的混合。符号是不可变的,这意味着它们在创建后无法更改。实现符号的方式,比较两个符号以查看它们是否相等非常有效,因此将它们用作哈希的键应该比使用字符串快一点。符号没有很多字符串所做的方法,例如start_with?,因此在调用这些方法之前,必须使用to_s将符号转换为字符串。

您可以在文档中阅读有关符号的更多信息:

more details about the new symbol GC

答案 4 :(得分:2)

Starting Ruby 2.2及以上Symbols会自动进行垃圾回收,所以这不应该是个问题。