Управление памятью Ruby - PullRequest
       11

Управление памятью Ruby

23 голосов
/ 08 октября 2008

Я уже давно пользуюсь Ruby и считаю, что для больших проектов он может занимать довольно много памяти. Каковы некоторые рекомендации по сокращению использования памяти в Ruby?

  • Пожалуйста, дайте каждому ответу иметь одну "лучшую практику", и пусть сообщество проголосует за нее.

Ответы [ 5 ]

27 голосов
/ 22 сентября 2010

При работе с огромными массивами объектов ActiveRecord будьте очень осторожны ... При обработке этих объектов в цикле, если на каждой итерации вы загружаете связанные с ними объекты с помощью has_many, Active_ecy ActiveRecord и т. Д. - использование памяти значительно увеличивается, каждый объект, принадлежащий массиву, растет ...

Нам очень помог следующий метод ( упрощенный пример ):

students.each do |student|
  cloned_student = student.clone
  ...
  cloned_student.books.detect {...}
  ca_teachers = cloned_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
  # Not sure if the following is necessary, but we have it just in case...
  cloned_student = nil
end

В приведенном выше коде "cloned_student" - это объект, который растет, но, поскольку он "обнуляется" в конце каждой итерации, это не проблема для огромного количества студентов. Если бы мы не делали «клонирование», переменная цикла «student» выросла бы, но поскольку она принадлежит массиву - используемая им память никогда не освобождается, пока существует объект массива.

Работает и другой подход:

students.each do |student|
  loop_student = Student.find(student.id) # just re-find the record into local variable.
  ...
  loop_student.books.detect {...}
  ca_teachers = loop_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
end

В нашей производственной среде у нас был фоновый процесс, который не удалось завершить один раз, потому что 8 ГБ оперативной памяти было недостаточно для него. После этого небольшого изменения он использует менее 1 ГБ для обработки того же объема данных ...

21 голосов
/ 08 октября 2008

Не злоупотребляйте символами.

Каждый раз, когда вы создаете символ, ruby ​​помещает запись в свою таблицу символов. Таблица символов - это глобальный хеш, который никогда не очищается.
Технически это не утечка памяти, но она ведет себя как единое целое. Символы не занимают много памяти, поэтому вам не нужно быть слишком параноиком, но стоит помнить об этом.

Общее указание: если вы на самом деле набрали символ в коде, это нормально (в конце концов, у вас только конечный объем кода), но не вызывайте to_sym для динамически генерируемых или вводимых пользователем строк, так открывает дверь для потенциально постоянно растущего числа

15 голосов
/ 08 октября 2008

Не делай этого:

def method(x)
  x.split( doesn't matter what the args are )
end

или это:

def method(x)
  x.gsub( doesn't matter what the args are )
end

Оба будут постоянно пропускать память в ruby ​​1.8.5 и 1.8.6 . (не уверен насчет 1.8.7, поскольку я не пробовал, но я действительно надеюсь, что это исправлено.) Обходной путь глуп и включает создание локальной переменной. Вам не нужно использовать местный, просто создайте его ...

Именно поэтому я очень люблю рубиновый язык, но не уважаю MRI

9 голосов
/ 08 октября 2008

Остерегайтесь C-расширений, которые сами выделяют большие куски памяти.

Например, когда вы загружаете изображение с помощью RMagick, все растровое изображение загружается в память внутри процесса ruby. Это может быть 30 мег или около того в зависимости от размера изображения.
Однако , большая часть этой памяти была выделена самим RMagick. Все, что знает ruby, это объект-обертка, который крошечный (1).
Ruby только думает, что удерживает небольшое количество памяти, поэтому не будет беспокоиться о работе GC. На самом деле он держится на 30 мег.
Если вы зациклите, скажем, 10 изображений, вы можете очень быстро исчерпать себя.

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

(1): Расширения Ruby C имеют обратные вызовы, которые запускаются, когда среда исполнения ruby ​​решает освободить их, поэтому в какой-то момент память будет успешно освобождена, но, возможно, не скоро.

0 голосов
/ 25 сентября 2016

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

Существует множество инструментов, таких как busy-administrator , которые позволяют проверять объем памяти объектов (включая те, которые находятся внутри хешей и массивов).

$ gem install busy-administrator

Пример # 1: MemorySize.of

require 'busy-administrator'

data = BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes)

puts BusyAdministrator::MemorySize.of(data)
# => 10 MiB

Пример # 2: MemoryUtils.profile

код

require 'busy-administrator'

results = BusyAdministrator::MemoryUtils.profile(gc_enabled: false) do |analyzer|
  BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes)
end  

BusyAdministrator::Display.debug(results)

Выход:

{
    memory_usage:
        {
            before: 12 MiB
            after: 22 MiB
            diff: 10 MiB
        }
    total_time: 0.406452
    gc:
        {
            count: 0
            enabled: false
        }
    specific:
        {
        }
    object_count: 151
    general:
        {
            String: 10 MiB
            Hash: 8 KiB
            BusyAdministrator::MemorySize: 0 Bytes
            Process::Status: 0 Bytes
            IO: 432 Bytes
            Array: 326 KiB
            Proc: 72 Bytes
            RubyVM::Env: 96 Bytes
            Time: 176 Bytes
            Enumerator: 80 Bytes
        }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...