Объекты ActiveRecord в хэшах не удаляются сборщиком мусора — ошибка или своего рода функция кэширования?

У меня есть простая модель 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.

ОБНОВЛЕНИЕ (РЕШЕНО)

Согласно приведенному ниже объяснению Оскара Дель Бена, в проблемном фрагменте кода создается несколько объектов ActiveRecord::Relation (на самом деле они созданы в обоих фрагментах кода, но по какой-то причине они «неправильно себя ведут» только во втором. Может ли кто-нибудь пролить свет на то, почему?). Они поддерживают ссылки на объекты ActiveRecord через переменную экземпляра с именем @records. Этой переменной экземпляра можно присвоить значение nil с помощью метода «reset» в ActiveRecord::Relation. Вы должны выполнить это на всех объектах отношения:

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гем, на который ссылается Оскар Дель Бен), хотя это будет намного сложнее. медленнее, чем код выше. Обратите внимание, что приведенный выше код не удаляет несколько объектов ActiveRecord::Relation из памяти. Хотя они кажутся довольно незначительными. Вы можете попробовать сделать:

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

И это удалит некоторые объекты ActiveRecord::Relation, но не все (не знаю почему, а те, что остались, не имеют Mass.references. Странно).

29
задан AmitA 1 July 2012 в 06:07
поделиться