Nell’articolo precedente abbiamo visto come creare un metodo che permetta di rilasciare le risorse al termine dell’utilizzo. Non sempre però è possibile determinare il momento del rilascio, ad esempio quando è legato ad eventi esterni. In questo caso è necessario che il distruttore sia eseguito proprio quando l’oggetto sia in fase di smaltimento, in automatico.
Con un linguaggio che adopera un garbage collector però bisogna fare molta attenzione per il semplice motivo che non si sa quando verrà eseguita la pulizia e solo in questo momento viene scaturito l’evento che richiama il metodo distruttore.
Quando distruggiamo esplicitamente un oggetto da codice, valorizzandolo con nil, stiamo solamente indicando l’intenzione, sarà poi compito del garbage collector analizzare se effettivamente non esiste nessun riferimento a quell’area di memoria, basta un solo indirizzamento anche involontario (i casi più probabili) per impedirne il rilascio. Poi c’è l’incognita di quando tutto ciò verrà fatto.
Riprendiamo l’esempio precedente con qualche aggiunta al codice:
class E #Creo una variabile di istanza, utile per i test attr_reader :var1 #Costruttore def initialize(var1) puts "Initializing #{self.object_id} var #{var1}" @var1=var1 end #Metodo di servizio def body begin yield rescue StandardError => err puts "Error: #{err}" ensure finalize end end #Metodo che fa il lavoro sporco def work puts 'Work' end private #Distruttore def finalize puts "End #{@var1}" end end #Istanzio il primo oggetto e1=E.new('Try1') e1.body do e1.work end #Istanzio il secondo oggetto per il quale ho anche la necessità #di rilasciare alcune risorse in un momento indeterminato e2=E.new('Try2') e2.body do ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"}) e2.work end puts 'Oggetti prima della pulizia:' ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"} #Distruggo gli oggetti e1=nil e2=nil puts 'Richiamo il garbage collector per la pulizia...' GC.start sleep(2) puts 'Oggetti dopo la pulizia:' ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}
Se eseguiamo questo script otterremo il seguente output:
D:\Marco\Progetti\Ruby\Batch\Test>ruby D2.rb
Initializing 20805980 var Try1
Work
End Try1
Initializing 20805910 var Try2
Work
End Try2
Oggetti prima della pulizia:
Oggetto: 20805910
Oggetto: 20805980
Richiamo il garbage collector per la pulizia...
Finalizing 20805910
Oggetti dopo la pulizia:
La definizione della classe è rimasta identica, questa volta stanzio due oggetti, il primo è identico all’articolo precedente, per il secondo invece ho ipotizzato che dovesse gestire il rilascio di alcune risorse in un momento x, noi non sappiamo quando si verificherà sappiamo solo che quando avverrà dobbiamo fare in modo che vengano deallocate alcune risorse.
Un esempio concreto è quando si scrive un file mediante un processo esterno, dobbiamo riferire di chiuderlo prima di terminare la nostra applicazione, anche se si verifica qualcosa di inatteso.
Se esaminiamo il secondo oggetto:
e2=E.new('Try2') e2.body do ObjectSpace.define_finalizer(e2, proc{|id|puts "Finalizing #{id}"}) e2.work end
Si nota che abbiamo fatto uso della classe ObjectSpace, contenitore di tutte le classi stanziate, per associare la dimissione dell’oggetto e2 alla proc passata come parametro. Tradotto in parole semplici stiamo dicendo: prima di distruggere l’oggetto esegui quella procedura.
Se riguardiamo l’output si può notare un altro l’utilizzo della classe ObjectSpace:
ObjectSpace.each_object(E){|o|puts "Oggetto: #{o.object_id}"}
E’ possibile esaminare ogni oggetto istanziato e specificando come parametro la classe E possiamo filtrare solo quella che ci interessa (attenzione che senza filtro la lista potrebbe essere molto lunga).
Tornando all’output: gli oggetti trovati nel contenitore sono due e viene mostrato l’id di riferimento. Dopo di che vengono distrutti e viene forzata l’esecuzione del garbage collector con GC.start. In alternativa a questo è anche possibile eseguire il metodo garbage_collect di ObjectSpace
Dopo la pulizia viene generato l’evento che richiama la proc associata all’oggetto, quella che nell’esempio scrive a video: Finalizing 20805910. L’id dell’oggetto è passato come parametro al blocco e noi lo mostriamo. Se andiamo a riesaminare il contenitore degli oggetti questa volta non conterrà nessuna istanza della classe E.
Nel prossimo articolo esaminerò l’utilizzo di metodi distruttori da associare all’evento di distruzione dell’oggetto. Tratterò anche la problematica legata alle variabili di istanza.
Italiano
English