Domanda Come rintracciare una perdita di memoria nel mio codice Ruby?


Domanda

Sto eseguendo il debug di una perdita di memoria in un'attività rake. Voglio vedere una serie di chiamate di:

  • Oggetti viventi
  • Quale oggetto o linea originariamente assegnava quegli oggetti

È possibile con ruby-prof?

In caso contrario, quale strumento dovrei usare?

Impostare

Gems

Rake task

  • Importa un file CSV direttamente in un database MySql utilizzando gli oggetti DATA LOAD INFILE e Active Record.

Quello che ho provato

Ho provato le modalità

  • RubyProf :: ACCANTONAMENTI
  • RubyProf :: MEMORIA

Tutto ciò che dice nella documentazione è:

RubyProf :: ACCANTONAMENTI I report di assegnazione degli oggetti mostrano il numero di oggetti allocati da ciascun metodo in un programma.

RubyProf :: MEMORIA I rapporti sull'utilizzo della memoria mostrano la quantità di memoria utilizzata da ciascun metodo in un programma.

Ciò implica che il ruby-prof si riferisca solo all'assegnazione totale di oggetti, non solo a quelli che stanno vivendo.

ho provato Ruby-Massa e Bloat Check ma nessuno dei due sembra essere in grado di fare ciò che voglio. Ruby-Mass si blocca anche perché trova oggetti FactoryGirl in memoria per qualche motivo ...


29
2018-01-06 18:09


origine


risposte:


Non ho trovato molto utile ruby-prof quando si trattava di localizzare perdite di memoria, perché è necessario un interprete Ruby con patch. Tracciare l'assegnazione degli oggetti è diventato più facile in Ruby 2.1. Forse è la scelta migliore per esplorare questo te stesso.

Raccomando il post del blog Ruby 2.1: objspace.so da parte di tmml che è uno degli sviluppatori principali di Ruby. Fondamentalmente puoi recuperare molte informazioni mentre esegui il debug dell'applicazione:

ObjectSpace.each_object{ |o| ... }
ObjectSpace.count_objects #=> {:TOTAL=>55298, :FREE=>10289, :T_OBJECT=>3371, ...}

require 'objspace'
ObjectSpace.memsize_of(o) #=> 0 /* additional bytes allocated by object */
ObjectSpace.count_tdata_objects #=> {Encoding=>100, Time=>87, RubyVM::Env=>17, ...}
ObjectSpace.count_nodes #=> {:NODE_SCOPE=>2, :NODE_BLOCK=>688, :NODE_IF=>9, ...}
ObjectSpace.reachable_objects_from(o) #=> [referenced, objects, ...]
ObjectSpace.reachable_objects_from_root #=> {"symbols"=>..., "global_tbl"=>...} /* in 2.1 */

Con Ruby 2.1 puoi anche iniziare a tracciare l'allocazione di nuovi oggetti e raccogliere metadati su ogni nuovo oggetto:

require 'objspace'
ObjectSpace.trace_object_allocations_start

class MyApp
  def perform
    "foobar"
  end
end

o = MyApp.new.perform
ObjectSpace.allocation_sourcefile(o) #=> "example.rb"
ObjectSpace.allocation_sourceline(o) #=> 6
ObjectSpace.allocation_generation(o) #=> 1
ObjectSpace.allocation_class_path(o) #=> "MyApp"
ObjectSpace.allocation_method_id(o)  #=> :perform

Uso leva e leva-debugger e inizia a esplorare l'heap di memoria dove pensi che probabilmente aumenterà, cercando rispettivamente segmenti diversi nel codice. Prima di Ruby 2.1 ho sempre fatto affidamento ObjectSpace.count_objects e calcolato la differenza del risultato, per vedere se un tipo di oggetto cresce in particolare.

La garbage collection funziona correttamente quando il numero di oggetti in crescita viene ritestato di nuovo a una quantità molto minore durante le iterazioni anziché continuare a crescere. Comunque, il netturbino dovrebbe correre tutto il tempo, puoi rassicurarti guardando nel Statistiche di Garbage Collector.

Dalla mia esperienza questa è una stringa o un simbolo (T_STRING). simboli prima di Ruby 2.2.0 non sono stati raccolti rifiuti, quindi assicurati che il tuo CSV o parti di esso non vengano convertiti in simboli lungo il percorso.

Se non ti senti a tuo agio, prova a eseguire il tuo codice sulla JVM con JRuby. Almeno la profilazione della memoria è supportata molto meglio con strumenti come VisualVM.


35
2018-01-06 18:43



Prendere in considerazione la gemma memory_profilerper l'uso con Ruby 2.1.


4
2017-11-10 15:41



C'è un ruby-mass gem, che fornisce una bella API su ObjectSpace.

Uno dei modi per affrontare il problema è controllare i riferimenti dopo aver finito con il tuo oggetto.

object = ...
# more logic
puts Mass.references(object)

Se esiste almeno un riferimento, l'oggetto non è garbage collection e è necessario capire come eliminare tale riferimento. Per esempio:

object.instance_variable_set("@example", nil)

# or

ObjectSpace.each_object(Your::Object::Class::Name).each do |obj|
  obj.instance_variable_set("@example", nil)
end

2
2017-10-24 06:51



Per risparmiare tempo è possibile controllare un elenco di gemme di Ruby che presentano prima perdite di memoria. https://github.com/ASoftCo/leaky-gems


2
2017-10-23 16:13