我有一个简单的 ActiveRecord 模型Student
,表中有 100 条记录。我在 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 对象(它们实际上是在两个代码片段中创建的,但由于某种原因,它们仅在第二个代码片段中“行为不端”。有人可以阐明为什么?)。它们通过称为@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。奇怪)。