Student
テーブルに 100 レコードで呼び出される単純な ActiveRecord モデルがあります。Rails コンソール セッションで次のことを行います。
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 を使用しています。
更新(解決済み)
以下の Oscar Del Ben の説明によると、いくつかの ActiveRecord::Relation オブジェクトが問題のあるコード スニペットで作成されます (実際には両方のコード スニペットで作成されますが、何らかの理由で 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
注: Mass.detach (参照されているruby -mass gem Oscar Del Ben を使用) を使用することもできますが、上記のコードよりもはるかに遅くなります。上記のコードは、一部の ActiveRecord::Relation オブジェクトをメモリから削除しないことに注意してください。ただし、これらはかなり重要ではないようです。あなたはやってみることができます:
Mass.index(ActiveRecord::Relation)["ActiveRecord::Relation"].each{|x| Mass.detach Mass[x]}
GC.start
そして、これにより ActiveRecord::Relation オブジェクトの一部が削除されますが、すべてではありません (理由は不明で、残っているオブジェクトには Mass.references がありません。奇妙です)。