Управление памятью  —  важнейший аспект разработки эффективных приложений на Ruby on Rails. В Ruby корректной работой с памятью, оптимизацией ее использования значительно повышается производительность приложения, снижаются затраты на сервер и совершенствуется пользовательское взаимодействие.

1. Модель памяти Ruby

Ruby  —  это язык с динамической типизацией и поддержкой сборки мусора. Управление памятью здесь в основном осуществляется такими компонентами:

  • Объектное пространство, где находятся все объекты Ruby. Когда создается новый объект, в этом пространстве выделяется память.
  • Куча  —  это участок памяти, используемый для выделения динамической памяти. Объекты Ruby размещаются в куче.
  • Сборщик мусора: в Ruby им автоматически высвобождается неиспользуемая память. Здесь это прежде всего сборщик по алгоритму маркировки и очистки.

Принцип работы сборщика мусора

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

2. Типичные проблемы управления памятью в Rails

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

3. Оптимизация использования памяти в Ruby on Rails

а) Недопущение дублирования объектов

  • Многократное создание объекта предотвращается мемоизацией.
  • Пример:
def expensive_operation   
@result ||= calculate_expensive_operation
end
  • Здесь дорогостоящая операция выполняется лишь раз, чем сокращается расход памяти.

б) Использование символов для неизменяемых строк

  • Символы сохраняются в памяти лишь раз, в ее расходовании они экономичнее, чем строки для неизменяемых значений.
  • Пример:
status = :active # По использованию памяти эффективнее, чем строка «active»

в) Оптимизация запросов ActiveRecord

  • Избегается загрузка лишних данных:
# Загружаются не все атрибуты, 
User.all
# а только необходимые
User.select(:id, :name)
  • Расход памяти снижается применением find_each для пакетной обработки:
User.find_each(batch_size: 1000) do |user|   
# Обрабатывается каждый пользователь
end

г) Сокращение объема занимаемой памяти при помощи промежуточного Rack

  • Промежуточным слоем вроде Rack::Deflater ответы сжимаются и расход памяти сокращается.
  • Пример:
# В «config/application.rb» 
config.middleware.use Rack::Deflater

д) Управление памятью в фоновых заданиях

  • Ограничивается количество записей, загружаемых в фоновые задания.
  • После выполнения задания подключения ActiveRecord явным образом очищаются:
ActiveRecord::Base.clear_active_connections!

4. Мониторинг и профилирование памяти

a) Применение GC.stat

  • Статистика сборки мусора в Ruby доступна с GC.stat.
  • Пример:
GC.start 
puts GC.stat
  • По GC.stat оценивается поведение сборщика мусора, настраивается производительность.

б) Инструменты для профилирования

  • Memory Profiler: библиотека для измерения расхода памяти и выявления ее раздувания.
require 'memory_profiler'  
report = MemoryProfiler.report do
# Код для профилирования
end

report.pretty_print
  • derailed_benchmarks: инструмент для тестирования использования памяти в Rails.
bundle exec derailed exec perf:mem
  • ObjectSpace: встроенный модуль Ruby для отслеживания объектов и расходуемой ими памяти.
ObjectSpace.each_object(SomeClass) { |obj| p obj }

5. Передовые методы управления памятью

a) Настройка сборщика мусора

  • Настраиваются такие параметры, как GC.start, GC::Profiler, и переменные окружения вроде RUBY_GC_HEAP_GROWTH_FACTOR.
  • Пример:
GC.start(full_mark: true, immediate_sweep: true)

б) Применение пулов объектов

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

в) Уменьшение объектов Ruby

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

Заключение

Управление памятью в Ruby on Rails важно для создания высокопроизводительных приложений. Благодаря пониманию модели памяти Ruby, оптимизированию использования ActiveRecord, применению символов и профилированию расходования памяти минимизируется ее раздувание, повышается эффективность приложения. Кроме того, регулярным мониторингом и тонкой настройкой сборщика мусора длительное время поддерживается оптимальная производительность.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи HEETESH PANGHANTI: Memory Management in Ruby and Rails

Предыдущая статьяРуководство по доступу к датчикам Android-устройств
Следующая статьяКодифицируйте схемы архитектуры AWS уже сегодня