Вот реальная причина разницы: строки никогда не бывают одинаковыми. Каждый экземпляр строки является отдельным объектом, даже если содержимое идентично. И большинство операций над строками создаст новые строковые объекты. Учтите следующее:
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
Они выглядят одинаковыми, но это разные объекты. Руби пришлось дважды выделить память, дважды выполнить метод String#initialize
и т. Д. Они занимают два отдельных места в памяти. И эй! Когда вы пытаетесь их изменить, становится еще веселее:
a += '' #=> 'zowie'
a.object_id #=> 2151845240
Здесь мы добавляем Ничего к a
и оставляем содержимое точно таким же - но Руби не знает этого. Он по-прежнему выделяет новый объект String, переназначает ему переменную a
, и старый объект String бездействует в ожидании возможной сборки мусора. Да, и пустая строка ''
также получает временный объект String, выделенный только на время этой строки кода. Попробуйте и посмотрите:
''.object_id #=> 2152710260
''.object_id #=> 2152694840
''.object_id #=> 2152681980
Быстрое ли распределение объектов на вашем гладком мульти-гигагерцовом процессоре? Конечно, они есть. Будут ли они жевать большую часть ваших 4 ГБ оперативной памяти? Нет, они не будут. Но сделайте это несколько миллионов раз, и это начнет складываться. Большинство приложений повсеместно используют временные строки, и ваш код, вероятно, полон строковых литералов внутри ваших методов и циклов. Каждый из этих строковых литералов и тому подобное будет выделять новый объект String, каждый раз, когда эта строка кода запускается. Настоящая проблема даже не в потере памяти; это время, потраченное впустую, когда сборка мусора запускается слишком часто, и ваше приложение начинает зависать.
Для сравнения взгляните на символы:
a = :zowie
b = :zowie
a.object_id #=> 456488
b.object_id #=> 456488
a == b #=> true
a.equal?(b) #=> true
Как только символ :zowie
будет создан, он никогда не станет другим. Каждый раз, когда вы ссылаетесь на данный символ, вы ссылаетесь на один и тот же объект. Там нет ни времени, ни памяти, потраченной на новые распределения. Это также может быть недостатком, если вы сходите с ума от них - они никогда не собирают мусор, поэтому, если вы начнете создавать бесчисленные символы динамически из пользовательского ввода, вы рискуете бесконечной утечкой памяти. Но для простых литералов в вашем коде, таких как постоянные значения или ключи хеша, они почти идеальны.
Это помогает? Дело не в том, что ваше приложение делает один раз. Это о том, что он делает миллионы раз.