Каковы ваши стратегии по снижению использования памяти?

StackOverflow https://stackoverflow.com/questions/144542

Вопрос

Рубин действительно требует памяти, но также стоит каждого бита.

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

Редактировать:Я нашел хорошую статью на эту тему здесь - старый, но все еще интересный.

Это было полезно?

Решение

  1. Выбирайте структуры дат, которые являются эффективным представлением, хорошо масштабируются и делают то, что вам нужно.
  2. Используйте алгоритмы, которые работают с использованием эффективных структур данных, а не раздутых, но более простых.
  3. Посмотрите еще где.В Ruby есть мост C, и в C гораздо проще учитывать память, чем в Ruby.

Другие советы

Я обнаружил, что Ruby Enterprise Edition от Phusion (вилка основной версии Ruby со значительно улучшенной сборкой мусора) существенно меняет использование памяти...Кроме того, они сделали его чрезвычайно простым в установке (и удалении, если вы обнаружите в этом необходимость).

Вы можете узнать больше и скачать его на их сайт.

Я действительно не думаю, что это так уж важно.Делать ваш код менее читаемым, чтобы улучшить потребление памяти, — это то, что вам следует делать только в том случае, если вы нуждаться это.Под необходимостью я имею в виду наличие конкретный случай для профиля производительности и конкретные показатели это указывает на то, что любое изменение решит проблему.

Если у вас есть приложение, в котором память будет ограничивающим фактором, то Ruby может оказаться не лучшим выбором.Тем не менее, я обнаружил, что мои приложения Rails обычно потребляют около 40-60 МБ ОЗУ на экземпляр Mongrel.По большому счету, это не так уж и много.

Возможно, вы сможете запустить свое приложение на JVM с помощью JRuby — виртуальная машина Ruby в настоящее время не так продвинута, как JVM, в управлении памятью и сборке мусора.В версию 1.9 добавлено множество улучшений, а также разрабатываются альтернативные виртуальные машины.

Разработчикам Ruby очень повезло, поскольку им не приходится самим управлять памятью.

Имейте в виду, что 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

Помните, что никогда раньше не преобразовывайте неконтролируемые аргументы в символы и не проверяйте аргументы, это может легко привести к отказу в обслуживании.

Также не забывайте избегать вложенных циклов глубиной более трех уровней, поскольку это усложняет обслуживание.Ограничение вложенности циклов и функций тремя или менее уровнями — хорошее практическое правило для поддержания производительности кода.

Вот несколько ссылок по этому поводу:

http://merbist.com

http://blog.monitis.com

  1. При развертывании веб-приложения Rails/Rack используйте REE или другой интерпретатор, поддерживающий копирование при записи.
  2. Настройте сборщик мусора (см. https://www.engineyard.com/blog/tuning-the-garbage-collector-with-ruby-1-9-2 например)
  3. Постарайтесь сократить количество используемых вами внешних библиотек/драймов, поскольку дополнительный код использует память.
  4. Если у вас есть часть вашего приложения, которая действительно интенсивно использует память, возможно, стоит переписать ее в расширении C или дополнить ее вызовом других/более быстрых/лучше оптимизированных программ (если вам приходится обрабатывать огромные объемы текстовых данных, возможно, вы можно заменить этот код вызовами grep, awk, sed и т. д.)

Я не разработчик Ruby, но думаю, что некоторые приемы и методы применимы к любому языку:

Используйте переменную минимального размера, подходящую для задания.
Уничтожить и закрыть переменные и соединения, когда они не используются.
Однако, если у вас есть объект, вам нужно будет использовать много раз подумайте о том, чтобы держать его в рамках любых циклов с манипуляциями с большой строкой DP, работа на меньшей строке, а затем добавьте к более крупной строке

Используйте приличную обработку ошибок (попробуйте поймать, наконец), чтобы убедиться, что объекты и соединения закрыты.

При работе с наборами данных возвращайте только минимально необходимый

За исключением крайних случаев, об использовании памяти не стоит беспокоиться.Время, которое вы потратите на попытки сократить использование памяти, окупится МНОГО гигабайт.

Взгляни на Программное обеспечение с малой памятью — шаблоны для систем с ограниченной памятью.Вы не указываете, какое ограничение памяти, но я предполагаю, что это ОЗУ.Я думаю, что в этой книге вы найдете несколько полезных идей, хотя и не относящихся к Ruby. Шаблоны охватывают ОЗУ, ПЗУ и вторичное хранилище и разделены на основные методы создания небольших структур данных, выделения памяти, сжатия, вторичного хранения и небольших архитектура.

Единственное, о чем нам действительно стоило беспокоиться, — это RMagick.

Решение состоит в том, чтобы убедиться, что вы используете RMagick версии 2, и вызвать Image#destroy! когда вы закончите использовать свое изображение

Избегайте такого кода:

str = ''
veryLargeArray.each do |foo|
  str += foo
  # but str << foo is fine (read update below)
end

который создаст каждое промежуточное строковое значение как объект String, а затем удалит его единственную ссылку на следующей итерации.Это засоряет память тоннами все более длинных строк, которые приходится собирать мусором.

Вместо этого используйте Array#join:

str = veryLargeArray.join('')

Это реализовано в C очень эффективно и не требует затрат на создание строк.

ОБНОВЛЯТЬ:Йонас прав в комментарии ниже.Мое предупреждение актуально для += но нет <<.

Я новичок в Ruby, но до сих пор не нашел необходимости делать что-то особенное в этом отношении (то есть, помимо того, что я обычно делаю как программист в целом).Возможно, это связано с тем, что память обходится дешевле, чем время, необходимое для ее серьезной оптимизации (мой код Ruby работает на машинах с 4–12 ГБ ОЗУ).Это также может быть связано с тем, что задания, для которых я его использую, не являются долгосрочными (т. е.это будет зависеть от вашего приложения).

Я использую Python, но думаю, стратегии схожи.

Я стараюсь использовать небольшие функции/методы, чтобы локальные переменные автоматически собирали мусор, когда вы возвращаетесь к вызывающему объекту.

В более крупных функциях/методах я явно удаляю большие временные объекты (например, списки), когда они больше не нужны.Закрытие ресурсов как можно раньше также может помочь.

Следует помнить о жизненном цикле ваших объектов.Если ваши объекты не передаются так часто, сборщик мусора в конечном итоге сработает и освободит их.Однако, если вы продолжаете ссылаться на них, сборщику мусора может потребоваться несколько циклов, чтобы освободить их.Это особенно верно в Ruby 1.8, где сборщик мусора использует плохую реализацию метода пометки и очистки.

Вы можете столкнуться с такой ситуацией, когда попытаетесь применить некоторые «шаблоны проектирования», такие как декоратор, которые сохраняют объекты в памяти в течение длительного времени.Это может быть неочевидно при изолированном примере, но в реальных приложениях, где одновременно создаются тысячи объектов, затраты на увеличение памяти будут значительными.

По возможности используйте массивы вместо других структур данных.Старайтесь не использовать числа с плавающей запятой, когда подойдут целые числа.

Будьте осторожны при использовании методов драгоценного камня/библиотеки.Возможно, они не оптимизированы для памяти.Например, класс Ruby PG::Result имеет неоптимизированный методvalues.Это будет использовать много дополнительной памяти.Мне еще предстоит сообщить об этом.

Замена реализации malloc(3) на Джемаллок немедленно уменьшит потребление памяти до 30%.Я создал драгоценный камень «jemalloc», чтобы добиться этого мгновенно.

Я стараюсь делать массивы, списки и наборы данных как можно меньшими.Отдельный объект не имеет большого значения, поскольку в большинстве современных языков создание и сборка мусора выполняются довольно быстро.

В тех случаях, когда вам нужно прочитать какой-то огромный набор данных из базы данных, обязательно читайте прямо/только и обрабатывайте его небольшими порциями, а не сначала загружайте все в память.

не используйте много символов, они остаются в памяти, пока процесс не будет завершен.это потому, что символы никогда не собирают мусор.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top