web-dev-qa-db-ja.com

Rubyコードでメモリリークを追跡するにはどうすればよいですか?

質問

Rakeタスクでメモリリークをデバッグしています。次のコールスタックを見たい:

  • 生き物
  • 最初にそれらのオブジェクトを割り当てたオブジェクトまたはライン

Ruby-profでこれは可能ですか?

そうでない場合、どのツールを使用すればよいですか?

セットアップ

宝石

レーキタスク

  • DATA LOAD INFILEおよびActive Recordオブジェクトを使用して、CSVファイルをMySqlデータベースに直接インポートします。

私が試したこと

私はモードを試しました

  • RubyProf :: ALLOCATIONS
  • RubyProf :: MEMORY

それがドキュメントで言うすべては:

RubyProf :: ALLOCATIONSオブジェクト割り当てレポートは、プログラムの各メソッドが割り当てるオブジェクトの数を示します。

RubyProf :: MEMORYメモリ使用量レポートは、プログラムの各メソッドが使用するメモリの量を示します。

これは、Ruby-profが、生存しているオブジェクトだけでなく、オブジェクトの合計割り当てについてレポートするだけであることを意味します。

私は Ruby-MassBloat Check を試しましたが、どちらも私が望むことを行うことができないようです。 Ruby-Massも何らかの理由でメモリ内のFactoryGirlオブジェクトを見つけているためクラッシュします...

30
John Gallagher

パッチを当てたRubyインタプリタが必要なため、メモリリークの特定に関しては、Ruby-profはあまり役に立ちませんでした。オブジェクト割り当ての追跡がRuby = 2.1。たぶん、これを自分で探索するのが最善の選択です。

Rubyコア開発者の1人であるtmmlによるブログ投稿 Ruby 2.1:objspace.so )をお勧めします。基本的にアプリケーションのデバッグ中に多くの情報を取得できます。

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

prypry-byebug を使用して、メモリヒープの探索を開始しますそれはおそらく成長するだろうと思います、あなたのコードでそれぞれ異なるセグメントを試してください。 Ruby 2.1以前は、常にObjectSpace.count_objectsを使用して結果の違いを計算し、1つのオブジェクトタイプが特に大きくなるかどうかを確認しました。

ガベージコレクションは、増加するオブジェクトの数が増加し続けるのではなく、反復中にはるかに少ない量に再テストされたときに正しく機能します。とにかく、ガベージコレクターは常に実行する必要があります。 ガベージコレクターの統計 を調べると、安心できます。

私の経験から、これは文字列または記号(T_STRING)のいずれかです。記号 before Ruby 2.2.0 はガベージコレクションされなかったため、CSVまたはその一部が変換されていないことを確認してください途中でシンボル。

不安を感じる場合は、JRubyを使用してJVMでコードを実行してみてください。少なくとも、メモリプロファイリングはVisualVMのようなツールでサポートされています。

35
Konrad Reiche

Ruby 2.1。で使用するために memory_profiler gem を検討してください。

4
Pistos

時間を節約するために、Rubyメモリリークのある宝石のリストを最初に確認できます。 https://github.com/ASoftCo/leaky-gems

3
Sergey Alekseev

ObjectSpaceに素敵なAPIを提供する Ruby-mass gemがあります。

この問題に対処する方法の1つは、オブジェクトを使い終わった後で参照を確認することです。

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

少なくとも1つの参照がある場合、オブジェクトはガベージコレクションされず、その参照を削除する方法を理解する必要があります。例えば:

object.instance_variable_set("@example", nil)

# or

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