que pasa oreo moto memoria manualmente los datos celular cache bueno borro borrar ruby ruby-on-rails-3.1 garbage-collection ruby-1.9.3

ruby - pasa - Los objetos ActiveRecord en hashes no se recolectan como basura, ¿un error o una especie de función de almacenamiento en caché?



es bueno borrar la memoria cache (2)

Tengo un modelo simple de ActiveRecord llamado Student con 100 registros en la tabla. Hago lo siguiente en una sesión de consola de rieles:

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!

Ahora hago lo siguiente:

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!

¿Alguien puede explicar por qué sucede esto y si existe una forma inteligente de resolverlo sin conocer la estructura de hash subyacente? Sé que puedo hacerlo:

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

y eliminará todos los objetos de Student de la memoria correctamente, pero me pregunto si hay una solución general (mi problema de la vida real está muy extendido y tiene estructuras de datos más complejas que el hash que se muestra arriba).

Estoy usando Ruby 1.9.3-p0 y Rails 3.1.0.

ACTUALIZACIÓN (RESUELTOS)

Según la explicación de Oscar Del Ben a continuación, algunos objetos ActiveRecord :: Relation se crean en el fragmento de código problemático (en realidad se crean en ambos fragmentos de código, pero por alguna razón se "portan mal" solo en el segundo. ¿Alguien puede arrojar luz sobre el ¿por qué?). Estos mantienen las referencias a los objetos ActiveRecord a través de una variable de instancia llamada @records. Esta variable de instancia se puede establecer en nula mediante el método de "restablecimiento" en ActiveRecord :: Relation. Debe asegurarse de realizar esto en todos los objetos de relación:

ObjectSpace.each_object(ActiveRecord::Base).count # => 100 ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset) GC.start ObjectSpace.each_object(ActiveRecord::Base).count # => 0

Nota: También puede usar Mass.detach (usando la gema de ruby-mass referencia Oscar Del Ben), aunque será mucho más lento que el código anterior. Tenga en cuenta que el código anterior no elimina algunos objetos ActiveRecord :: Relation de la memoria. Sin embargo, estos parecen ser bastante insignificantes. Puedes intentar hacer:

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

Y esto eliminaría algunos de los objetos ActiveRecord :: Relation, pero no todos (no estoy seguro de por qué, y los que quedan no tienen referencias Mass. Raras).


Yo no se la respuesta

Pero intenté inspeccionar el montón como se muestra en http://blog.headius.com/2010/07/browsing-memory-jruby-way.html

He adjuntado una captura de pantalla en https://skitch.com/deepak_kannan/en3dg/java-visualvm fue un programa simple

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

Luego usé jvisualvm como se indica arriba. Estaba ejecutando esto en irb.
Parece como si jruby está rastreando el alcance del objeto. El objeto no obtendrá GC''ed si hay alguna referencia no débil a ese objeto


Creo que sé lo que está pasando. El GC de Ruby no libera objetos inmutables (¡como símbolos!). Las claves devueltas por group_by son cadenas inmutables, por lo que no se recogerán en la basura.

ACTUALIZACIÓN :

Parece que el problema no es con Rails en sí. Intenté usar group_by solo, y algunas veces los objetos no se recolectaban basura:

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

He explorado los componentes internos de GC (que son sorprendentemente fáciles de entender), y esto parece ser un problema de alcance. Ruby recorre todos los objetos en el alcance actual y marca los que cree que aún se están utilizando, luego pasa por todos los objetos del montón y libera los que no han sido marcados.

En este caso, creo que el hash todavía se está marcando a pesar de que está fuera del alcance. Hay muchas razones por las que esto puede suceder. Seguiré investigando.

ACTUALIZACIÓN 2:

He encontrado lo que guarda referencias de objetos. Para ello he usado la gema ruby-mass . Resulta que la relación de registro activo realiza un seguimiento de los objetos devueltos.

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

Desafortunadamente, el reset llamadas en la relación no pareció ayudar, pero espero que esta sea suficiente información por ahora.