2012-06-22 22 views
26

テーブルに100レコードのStudentという単純なActiveRecordモデルがあります。私は、レールのコンソールセッションで次の操作を行いますハッシュのActiveRecordオブジェクトはガベージコレクションされません - バグや一種のキャッシュ機能?

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

x = Student.all 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

x = nil 
GC.start 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0  # Good! 

は今、私は次のようにします。

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

x = Student.all.group_by(&:last_name) 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

x = nil 
GC.start 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100  # Bad! 

この問題が発生したと根本的に知らなくても、これを解決するスマートな方法があるかどうか、なぜ誰もが説明できますハッシュ構造?私はこれを行うことができます知っている:

x.keys.each{|k| x[k]=nil} 
x = nil 
GC.start 

をし、それが正しくメモリからすべてのStudentオブジェクトを削除しますが、一般的な解決策がある場合、私は思ったんだけど(私の現実の問題が広く普及し、より複雑なデータを持っています上記のハッシュよりも構造が複雑です)。

私はRuby 1.9.3-p0とRails 3.1.0を使用しています。

UPDATE(解決しよう)

以下

パーオスカー・デル・ベンの説明、いくつかのActiveRecord ::関連オブジェクトは(彼らは実際に両方のコードスニペットで作成されますが、問題のコードスニペットで作成されたいくつかの理由のために、彼ら第2のものでのみ「誤動作」があります。誰かがなぜその理由を明らかにすることができますか?)。これらは、@recordsというインスタンス変数を介してActiveRecordオブジェクトへの参照を保持します。このインスタンス変数は、ActiveRecord :: Relationの "reset"メソッドによってnilに設定できます。あなたはすべての関係オブジェクトでこれを実行することを確認する必要があります。

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset) 

GC.start 
ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

注:それはコードよりもはるかに遅くなりますけれどもまた、(オスカー・デル・ベンが参照ruby-mass宝石を使用して)Mass.detachを使用することができます上記。上記のコードは、メモリからいくつかのActiveRecord :: Relationオブジェクトを削除しないことに注意してください。しかし、これらはかなり重要ではないようです。あなたはやって試すことができます:

Mass.index(ActiveRecord::Relation)["ActiveRecord::Relation"].each{|x| Mass.detach Mass[x]} 
GC.start 

そして、これはActiveRecordの::関係オブジェクトの一部を削除し、すべてではなく、それらのでしょう(わからない理由、そして残されたものは何Mass.references奇妙なを持っていません。)。

+0

1.9または3.1に固有の場合があります - この動作はRails 3.0.7およびruby enterprise(ree 1.8.7)では表示されません。 – klochner

+0

ありがとうございますKlochner! Ruby 1.8.7-p174でコードを実行しました。 Ruby 1.8.7では、Rails 3.0.7とRails 3.1.0の両方でオブジェクトの破壊が正しく処理されているようです。私。 2番目の例では、0個のオブジェクトを取得します。私もRuby 1.9.2を試しましたが、同じ問題は1.9.3と同じように起こります。あなたはYARVにバグがあると思いますか? – AmitA

+0

私はRuby 1.8.7とRails 2.3.12でテストを行った。私はコンソールでのみテストし、同じ問題がありました。 ** **を除いて、 'asdasdsa 'のようなコンソールにゴミを書き、' NameError'を開始しました。この 'GC.start'の後にすべてがクリーンアップされました。好奇心の強い副作用かもっと重要なことがあるかどうかは不明です。 – Casper

答えて

10

私は何が起こっているのか知っていると思います。 RubyのGCは、フリーの不変オブジェクト(シンボルのようなもの)を手に入れません。 group_byが返すキーは不変の文字列なので、ガベージコレクションされません。

UPDATE

問題はRailsの自体ではないように思えます。私は一人でGROUP_BY使用してみました、とオブジェクトがガベージコレクションを取得しません時々:私は(理解し、驚くほど簡単です)GCの内部を通じて掘ってきた

oscardelben~/% irb 
irb(main):001:0> class Foo 
irb(main):002:1> end 
=> nil 
irb(main):003:0> {"1" => Foo.new, "2" => Foo.new} 
=> {"1"=>#<Foo:0x007f9efd8072a0>, "2"=>#<Foo:0x007f9efd807250>} 
irb(main):004:0> ObjectSpace.each_object(Foo).count 
=> 2 
irb(main):005:0> GC.start 
=> nil 
irb(main):006:0> ObjectSpace.each_object(Foo).count 
=> 0 
irb(main):007:0> {"1" => Foo.new, "2" => Foo.new}.group_by 
=> #<Enumerator: {"1"=>#<Foo:0x007f9efb83d0c8>, "2"=>#<Foo:0x007f9efb83d078>}:group_by> 
irb(main):008:0> GC.start 
=> nil 
irb(main):009:0> ObjectSpace.each_object(Foo).count 
=> 2 # Not garbage collected 
irb(main):010:0> GC.start 
=> nil 
irb(main):011:0> ObjectSpace.each_object(Foo).count 
=> 0 # Garbage collected 

、これはスコープの問題のように思えます。 Rubyは、現在のスコープ内のすべてのオブジェクトを歩き、まだ使用されていると思うものをマークした後、すべてをヒープ内のオブジェクトに移動し、を持たないオブジェクトを解放します。がマークされています。

この場合、範囲外であってもハッシュがまだマークされていると思います。これが起こるのには多くの理由があります。私は調査を続けます。

UPDATE 2:

オブジェクトの参照を保持しているもの、私を見つけました。そのために、私はruby mass gemを使用しました。 Active Recordの関係は、返されたオブジェクトを追跡していることが分かります。

User.limit(1).group_by(&:name) 
GC.start 
ObjectSpace.each_object(ActiveRecord::Base).each do |obj| 
    p Mass.references obj # {"ActiveRecord::Relation#70247565268860"=>["@records"]} 
end 

残念ながら、関係にresetを呼び出すと、助けていないようでしたが、うまくいけば、これは今のところ十分な情報です。

+0

素晴らしい。 thanks – deepak

+1

ええと。あなたは 'group_by'によって返されたハッシュがまだ解放されるべきだと思います。そしてそのすべての要素がハッシュに(鍵を除いて)添付されています。だから、これは私には意味をなさない。それ以外の場合は、キーでシンボルを書いたすべてのハッシュは永遠にメモリに残ります。 – Casper

+0

キャスパー、私は確かに分かりませんが、それは起こっていると思います。 –

2

https://skitch.com/deepak_kannan/en3dg/java-visualvm は、それが次に使用される単純なプログラム

class Foo; end 
f1 = Foo.new 
f2 = Foo.new 
GC.start 

だった、私は答え

を知らないが、私はでスクリーンショットを添付したhttp://blog.headius.com/2010/07/browsing-memory-jruby-way.html

に与えられたヒープを検査しようとしました上記のようにjvisualvm。これをirbで実行していた。
jrubyがオブジェクトのスコープを追跡しているかのようです。オブジェクトに非弱い参照がある場合、オブジェクトはGCされません。

+0

irb like:jruby -J-Djruby.reify.classes = true -X + O 'which irb' – deepak

+0

Deepakにお返事ありがとうございます。私はJRubyを使っていません。 JRubyに投稿したコードを試しましたか? JRubyのGCは自分のコードに関してMRIとは異なる動作をしますか? – AmitA

+0

@AmitAあなたが投稿したコードは試していませんでした。デバッグや視覚化が容易なため、よりシンプルなバージョンを試してみました。具体的には、私はヒープをダンプしたい、MRIでいくつかの拡張を試みたが、コンパイルしなかった。 JRubyはJVM上で動作するので、GCはMRIとは異なります。 – deepak

関連する問題