Подтвердить что ты не робот

Как отслеживать утечку памяти в моем Ruby-коде?

Вопрос

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

  • Живые объекты
  • Какой объект или строка изначально были выделены для этих объектов

Возможно ли это с ruby-prof?

Если нет, какой инструмент я должен использовать?

Настройка

Драгоценные камни

Задача Rake

  • Импортирует CSV файл непосредственно в базу данных MySql с использованием объектов DATA LOAD INFILE и Active Record.

Что я пробовал

Я пробовал режимы

  • RubyProf:: РАСПРЕДЕЛЕНИЯ
  • RubyProf:: ПАМЯТЬ

В документации все сказано:

RubyProf:: РАСПРЕДЕЛЕНИЯ Отчеты о распределении объектов показывают, сколько объектов выделяет каждый метод в программе.

RubyProf:: ПАМЯТЬ Отчеты об использовании памяти показывают, сколько памяти использует каждый метод в программе.

Это означает, что ruby-prof просто сообщает об общем распределении объектов, а не только о тех, которые живут.

Я пробовал Ruby-Mass и Bloat Check, но, похоже, что-то не получается Я хочу. Ruby-Mass также выходит из строя, потому что по какой-то причине находит объекты FactoryGirl в памяти...

4b9b3361

Ответ 1

Я не нашел ruby-prof очень полезным, когда дело доходит до обнаружения утечек памяти, потому что вам нужен исправленный интерпретатор Ruby. Отслеживание размещения объектов стало проще в Ruby 2.1. Может быть, это лучший выбор, чтобы изучить это самостоятельно.

Я рекомендую пост в блоге Ruby 2.1: objspace.so от tmml, который является одним из разработчиков ядра Ruby. В основном вы можете получить много информации при отладке вашего приложения:

ObjectSpace.each_object{ |o| ... }
ObjectSpace.count_objects #=> {:TOTAL=>55298, :FREE=>10289, :T_OBJECT=>3371, ...}

require 'objspace'
ObjectSpace.memsize_of(o) #=> 0 /* additional bytes allocated by object */
ObjectSpace.count_tdata_objects #=> {Encoding=>100, Time=>87, RubyVM::Env=>17, ...}
ObjectSpace.count_nodes #=> {:NODE_SCOPE=>2, :NODE_BLOCK=>688, :NODE_IF=>9, ...}
ObjectSpace.reachable_objects_from(o) #=> [referenced, objects, ...]
ObjectSpace.reachable_objects_from_root #=> {"symbols"=>..., "global_tbl"=>...} /* in 2.1 */

С Ruby 2.1 вы можете даже начать отслеживать размещение новых объектов и собирать метаданные о каждом новом объекте:

require 'objspace'
ObjectSpace.trace_object_allocations_start

class MyApp
  def perform
    "foobar"
  end
end

o = MyApp.new.perform
ObjectSpace.allocation_sourcefile(o) #=> "example.rb"
ObjectSpace.allocation_sourceline(o) #=> 6
ObjectSpace.allocation_generation(o) #=> 1
ObjectSpace.allocation_class_path(o) #=> "MyApp"
ObjectSpace.allocation_method_id(o)  #=> :perform

Используйте Поддеть и подглядывать-byebug и начать изучать кучу памяти, где вы думаете, это будет, вероятно, расти, соответственно попробовать различные сегменты кода. До Ruby 2.1 я всегда полагался на ObjectSpace.count_objects и вычислял разницу в результатах, чтобы посмотреть, особенно ли растет один тип объекта.

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

По моему опыту это либо строка, либо символ (T_STRING). Символы до ruby 2.2.0 не собирались мусором, поэтому убедитесь, что ваш CSV или его части не преобразуются в символы на пути.

Если вам неудобно, попробуйте запустить свой код на JVM с JRuby. По крайней мере, профилирование памяти намного лучше поддерживается такими инструментами, как VisualVM.

Ответ 3

Существует ruby-mass gem, который обеспечивает приятный api над ObjectSpace.

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

object = ...
# more logic
puts Mass.references(object)

Если есть хотя бы одна ссылка, объект не собирает мусор, и вам нужно выяснить, как удалить эту ссылку. Например:

object.instance_variable_set("@example", nil)

# or

ObjectSpace.each_object(Your::Object::Class::Name).each do |obj|
  obj.instance_variable_set("@example", nil)
end

Ответ 4

Чтобы сэкономить время, вы можете проверить список драгоценных камней Ruby, которые сначала имеют утечку памяти. https://github.com/ASoftCo/leaky-gems