Рубин действительно требует памяти, но также стоит каждого бита.
Что вы делаете, чтобы использовать память на низком уровне? Избегаете ли вы больших строк и вместо этого используете меньшие массивы / хэши или это не проблема, о чем вы можете позаботиться и позволить сборщику мусора делать свою работу?
Редактировать: Я нашел хорошую статью на эту тему. здесь - старая, но все еще интересная.

Я не разработчик Ruby, но думаю, что некоторые приемы и методы применимы к любому языку:
Используйте переменную минимального размера, подходящую для задания
Удалите и закройте переменные и соединения, когда они не используются
Однако, если у вас есть объект, который вам нужно будет использовать много раз, подумайте о том, чтобы оставить его в области видимости.
Любые циклы с манипуляциями с большой строкой выполняются с меньшей строкой, а затем добавляются к большей строке
Используйте приличную (наконец, попробуйте поймать) обработку ошибок, чтобы убедиться, что объекты и соединения закрыты.
При работе с наборами данных возвращайте только необходимый минимум
плохой совет. Держите переменные в области видимости дольше, но строки убивают память - добавление делает слишком много повторных блоков памяти, поэтому добавление может даже быть вашей проблемой в первую очередь.
Да, но иногда неизбежно приходится добавлять строку и т. д. Лучше проделать любую работу с меньшей строкой, а затем добавить ее к большей. В .net я бы использовал для этого построитель строк. Он хотел уменьшить использование памяти, и если вы не использовали переменную, зачем ее хранить?
«Используйте переменную минимального размера, подходящую для работы» - это не относится к Ruby.
Я новичок в Ruby, но пока не считаю необходимым делать что-то особенное в этом отношении (то есть помимо того, что я обычно делаю как программист). Возможно, это связано с тем, что память дешевле, чем время, которое потребовалось бы для ее серьезной оптимизации (мой код Ruby работает на машинах с 4–12 ГБ ОЗУ). Это также может быть связано с тем, что задания, для которых я его использую, не выполняются долго (т.е. это будет зависеть от вашего приложения).
Я обнаружил, что Ruby Enterprise Edition от Phusion (ответвление основного Ruby со значительно улучшенной сборкой мусора) существенно изменил использование памяти ... Кроме того, они сделали его чрезвычайно простым в установке (и удалении, если вы найти потребность).
Вы можете узнать больше и скачать его на их сайт.
За исключением крайних случаев использование памяти не о чем беспокоиться. На время, которое вы потратите на сокращение использования памяти, вы купите МНОГО гигабайт.
Я действительно не думаю, что это так уж важно. Сделать ваш код менее читаемым для улучшения потребления памяти - это то, что вы должны делать, только если вы его необходимость. И под необходимостью я имею в виду наличие конкретный случай для профиля производительности и конкретные показатели, которые указывают, что любое изменение решит проблему.
Если у вас есть приложение, в котором память будет ограничивающим фактором, то Ruby может быть не лучшим выбором. Тем не менее, я обнаружил, что мои приложения Rails обычно потребляют около 40-60 МБ ОЗУ на один экземпляр Mongrel. По сути, это не так уж и много.
Возможно, вы сможете запустить свое приложение на JVM с JRuby - виртуальная машина Ruby в настоящее время не так продвинута, как JVM, для управления памятью и сборки мусора. В выпуске 1.9 добавлено много улучшений, а также в разработке находятся альтернативные виртуальные машины.
Проблема не в том, что экземпляру mongrel требуется 60M - это больше, что мне приходится перезапускать mongrel каждый день, чтобы избежать огромной утечки - но я имел в виду не это, задавая этот вопрос. ;)
Совершенно верно. Я думаю, что одна из БОЛЬШИХ выгод от Passenger заключается в управлении экземплярами. Я еще не переставлял свой сервер, но собираюсь сделать это в ближайшее время.
На самом деле я сейчас предпочитаю Thin, а не Mongrel, но проблема во внутреннем Ruby. Включает ли 1.9 какие-либо исправления для сборщика мусора?
Проблема в том, что JVM добавляет целый набор проблем с производительностью.
Я стараюсь, чтобы массивы, списки и наборы данных были как можно меньше. Отдельный объект не имеет большого значения, поскольку создание и сборка мусора на большинстве современных языков выполняется довольно быстро.
В тех случаях, когда вам нужно прочитать какой-то огромный набор данных из базы данных, убедитесь, что вы читаете в режиме прямого / только и обрабатываете его маленькими битами, а не сначала загружаете все в память.
С-образный мост плохо документирован, и вам всегда придется смотреть самому. Большинство ссылок Google - это устаревшие документы / ссылки на несуществующие страницы. ;)
Взгляните на Программное обеспечение с малой памятью - шаблоны для систем с ограниченной памятью. Вы не указываете, какое ограничение памяти, но я предполагаю RAM. Хотя это и не относится к Ruby, я думаю, вы найдете в этой книге несколько полезных идей - шаблоны охватывают RAM, ROM и вторичное хранилище и делятся на основные методы малых структур данных, распределения памяти, сжатия, вторичного хранилища и малого архитектура.
Избегайте такого кода:
str = ''
veryLargeArray.each do |foo|
str += foo
# but str << foo is fine (read update below)
end
который создаст каждое промежуточное строковое значение как объект String, а затем удалит его единственную ссылку на следующей итерации. Это заполняет память тоннами все более длинных строк, которые необходимо собирать сборщиком мусора.
Вместо этого используйте Array#join:
str = veryLargeArray.join('')
Это реализовано в C очень эффективно и не требует накладных расходов на создание String.
ОБНОВЛЕНИЕ: Джонас прав в комментарии ниже. Мое предупреждение относится к +=, но не к <<.
+= has that problem but << is an append.
Это хорошо знать! Я обновил свой ответ. Таким образом, << деструктивно обновляет вызываемую строку, а не возвращает новую строку, как это делает +=. Спасибо!
Я использую Python, но думаю, что стратегии похожи.
Я стараюсь использовать небольшие функции / методы, чтобы локальные переменные автоматически собирали мусор, когда вы возвращаетесь к вызывающей стороне.
В более крупных функциях / методах я явно удаляю большие временные объекты (например, списки), когда они больше не нужны. Как можно раньше закрытие ресурсов тоже может помочь.
Это решение, но в Ruby нет возможности явно удалить объект. Нет даже деструкторов. Иногда Ruby-way немного странный. ;)
Или наоборот, деструкторы не на Ruby странны :) Если копать дальше, деструкторы / финализаторы на самом деле являются странная концепция и реализованы каким-то странным образом на многих языках. Но если вам это действительно нужно, см. этот вопрос, чтобы узнать о ObjectSpace.define_finalizer. Конечно, этот метод, не похожий на ООП, является странным и обескураживающим, потому что финализаторы странны и не приветствуются :)
не используйте много символов, они остаются в памяти до тех пор, пока процесс не будет убит ... это потому, что символы никогда не собираются сборщиком мусора.
Скорее: используйте много символов, но помните их цель. Никогда не относитесь к ним как к легким струнам. [ruby-lang.org/en/documentation/ruby-from-other-languages/]
Есть ли доказательства того, что загрузка кода занимает много памяти?
Если я создаю новое приложение Rails и запускаю консоль, оно использует около 65 МБ памяти. Если я добавлю Gemfile большого приложения Rails и снова запущу консоль, он будет использовать около 180 МБ памяти.
ссылка не работает!
Ссылка @Cyzanfar исправлена, но я уверен, что где-то еще можно найти более свежую информацию;)
Разработчикам Ruby очень повезло, поскольку им не нужно управлять памятью самостоятельно.
Имейте в виду, что рубин выделяет объекты, например такие простые вещи, как
100.times{ 'foo' }
выделяет 100 строковых объектов (строки изменяемы, и для каждой версии требуется собственное выделение памяти).
Убедитесь, что если вы используете библиотеку, выделяющую много объектов, другие альтернативы недоступны, и ваш выбор стоит того, чтобы оплатить сборщик мусора. (у вас может быть не так много запросов / с или несколько десятков мс на запросы).
Создание хеш-объекта действительно выделяет больше, чем объект, например
{'joe' => 'male', 'jane' => 'female'}
не выделяет 1 объект, а 7. (один хэш, 4 строки + 2 строки ключей)
Если вы можете использовать символьные ключи, поскольку они не будут собираться мусором. Однако, поскольку они не будут собираться сборщиком мусора, вы не должны использовать полностью динамические ключи, такие как преобразование имени пользователя в символ, в противном случае вы «утечите» память.
Пример: Где-то в вашем приложении вы применяете to_sym к имени пользователя, например:
hash[current_user.name.to_sym] = something
Когда у вас сотни пользователей, это нормально, но что происходит, если у вас миллион пользователей? Вот цифры:
ruby-1.9.2-head >
# Current memory usage : 6608K
# Now, add one million randomly generated short symbols
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s).to_sym }
# Current memory usage : 153M, even after a Garbage collector run.
# Now, imagine if symbols are just 20x longer than that ?
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s * 20).to_sym }
# Current memory usage : 501M
Помните, что никогда не преобразовывайте неконтролируемые аргументы в символы или проверяйте аргументы раньше, это может легко привести к отказу в обслуживании.
Также не забывайте избегать вложенных циклов глубиной более трех уровней, поскольку это затрудняет обслуживание. Ограничение вложенности циклов и функций тремя или менее уровнями - хорошее практическое правило, позволяющее поддерживать работоспособность кода.
Вот несколько ссылок относительно:
Следует помнить о жизненном цикле ваших объектов. Если ваши объекты не передаются так часто, сборщик мусора в конечном итоге сработает и освободит их. Однако, если вы продолжите ссылаться на них, сборщику мусора может потребоваться несколько циклов, чтобы их освободить. Это особенно верно в Ruby 1.8, где сборщик мусора использует плохую реализацию метода отметки и очистки.
Вы можете столкнуться с этой ситуацией, когда попытаетесь применить некоторые «шаблоны проектирования», такие как декоратор, которые надолго сохраняют объекты в памяти. Это может быть неочевидно при тестировании отдельного примера, но в реальных приложениях, где одновременно создаются тысячи объектов, затраты на увеличение памяти будут значительными.
По возможности используйте массивы вместо других структур данных. Старайтесь не использовать числа с плавающей запятой, когда подойдут целые числа.
Будьте осторожны при использовании методов гемов / библиотек. Возможно, они не оптимизированы для памяти. Например, класс Ruby PG :: Result имеет метод values, который не оптимизирован. Это будет использовать много дополнительной памяти. Я еще не сообщал об этом.
Где вы узнали, что массивы отличаются от других структур данных с точки зрения памяти и что числа с плавающей запятой занимают память?
Обычно целые числа занимают 4 байта, а числа с плавающей запятой занимают 8 байтов. stackoverflow.com/questions/5067249/…
Кроме того, предполагается, что массивы обычно требуют меньше накладных расходов. Конечно, разные реализации Ruby могут отличаться. Но это хорошее практическое правило.
Замена реализации malloc (3) на джемаллок немедленно снизит потребление памяти до 30%. Я создал драгоценный камень jemalloc, чтобы добиться этого мгновенно.
В Ruby это немного хуже, потому что сборка мусора может занять много времени, и вы не можете напрямую уничтожить переменную.