我读过的每一篇关于 Ruby 符号的文章都谈到了符号对字符串的效率。但是,这不是 1970 年代。我的电脑可以处理一些额外的垃圾收集。我错了吗?我有最新最好的奔腾双核处理器和 4 gigs 的 RAM。我认为这应该足以处理一些字符串。
5 回答
您的计算机很可能能够处理“一点点额外的垃圾收集”,但是当那个“一点点”发生在运行数百万次的内部循环中时呢?当它在内存有限的嵌入式系统上运行时呢?
有很多地方你可以随意使用字符串,但在某些地方你不能。这一切都取决于上下文。
的确,出于记忆的原因,您并不需要如此糟糕的令牌。毫无疑问,您的计算机可以处理各种复杂的字符串处理。
但是,除了更快之外,令牌还具有额外的优势(尤其是上下文着色),即在视觉上尖叫:看我,我是键值对中的键。这是为我使用它们的充分理由。
还有其他原因……而且其中许多的性能提升可能比您意识到的要多,尤其是在进行比较之类的操作时。
当比较两个 ruby 符号时,解释器只是比较两个对象地址。比较两个字符串时,解释器必须一次比较每个字符。如果你做了很多这样的计算,那么这种计算就会加起来。
符号有其自身的性能问题......它们永远不会被垃圾收集。
这篇文章值得一读: http ://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol
很好地保证符号是唯一的——这可以产生一些你不会从 String 获得的很好的效果(比如我相信它们的地址总是完全相等的)。
另外它们有不同的含义,你会想在不同的领域使用它们,但是 ruby 对那种东西并不太严格,所以我可以理解你的问题。
这是造成差异的真正原因:字符串永远不会相同。字符串的每个实例都是一个单独的对象,即使内容相同。大多数对字符串的操作都会生成新的字符串对象。考虑以下:
a = 'zowie'
b = 'zowie'
a == b #=> true
从表面上看,很容易声称a
并且b
是相同的。大多数常识操作都会按您的预期工作。但:
a.object_id #=> 2152589920 (when I ran this in irb)
b.object_id #=> 2152572980
a.equal?(b) #=> false
它们看起来一样,但它们是不同的对象。Ruby 必须分配两次内存,两次执行该String#initialize
方法,等等。它们占用了内存中的两个不同位置。嘿!当您尝试修改它们时,它会变得更加有趣:
a += '' #=> 'zowie'
a.object_id #=> 2151845240
在这里,我们什么也不添加,a
保持内容完全相同——但 Ruby 不知道这一点。它仍然分配一个全新的 String 对象,将变量重新分配a
给它,而旧的 String 对象则坐在那里等待最终的垃圾回收。哦,空''
字符串也会在该行代码的持续时间内获得一个临时的 String 对象。试试看:
''.object_id #=> 2152710260
''.object_id #=> 2152694840
''.object_id #=> 2152681980
这些对象分配在您光滑的多千兆赫处理器上是否快速?当然是。他们会吃掉你 4 GB 的大部分内存吗?不,他们不会。但是重复几百万次,它就会开始累加。大多数应用程序到处都使用临时字符串,并且您的代码可能在您的方法和循环中充满了字符串文字。每次运行该行代码时,每个字符串文字等都会分配一个新的 String 对象。真正的问题甚至不是内存浪费。当垃圾收集被过于频繁地触发并且您的应用程序开始挂起时,这是浪费的时间。
相比之下,看一下符号:
a = :zowie
b = :zowie
a.object_id #=> 456488
b.object_id #=> 456488
a == b #=> true
a.equal?(b) #=> true
一旦符号:zowie
被制作出来,它就永远不会再制作一个。每次你引用一个给定的符号时,你指的是同一个对象。没有时间或内存浪费在新的分配上。如果你对它们过于疯狂,这也可能是一个缺点——它们永远不会被垃圾收集,所以如果你开始从用户输入动态创建无数符号,你就有无穷无尽的内存泄漏的风险。但是对于代码中的简单文字,例如常量值或哈希键,它们几乎是完美的。
这有帮助吗?这与您的应用程序执行一次操作无关。这是关于它做了几百万次的事情。
少一个字符要输入。这就是我需要将它们用于哈希键等字符串的所有理由。