alessandro andreose’ - helpdesk website studio e... · il metodo finalize (distruttore) metodo...

32
Alessandro Andreose’

Upload: truongdang

Post on 16-Feb-2019

218 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Alessandro Andreose’

Page 2: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Quando? Quando parte il Garbage Collector?

Viene eseguito dal .NET Framework quando lo ritiene necessario

Quando non c’è abbastanza spazio nello heap

Page 3: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Cosa fa? Cosa succede quando il GC viene eseguito?

GC controlla tutti gli oggetti nello heap

Segna tutti gli oggetti raggiungibili direttamente e indirettamente da un riferimento

Rilascia la memoria di tutti gli oggetti non contrassegnati

Ricompatta lo heap

Page 4: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Problema

Finalizzazione non deterministica

Page 5: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Il metodo finalize (Distruttore) Metodo speciale

Il garbage collector invoca questo metodo prima di rilasciare la memoria allocata per l’oggetto

Nel metodo finalize non si deve accedere ad oggetti esterni.

Potrebbero essere già stati distrutti dal GC

NotaIl metodo viene eseguito quando il GC deve rilasciare la memoriaMa io non so quando il GC dovrà fare questa operazione

Page 6: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

finalize In c# non è possibile chiamare o sottoporre ad override il metodo

Object.Finalize() In c# i distruttori vengono utilizzati per la scrittura del codice di

finalizzazione

Il distruttore si dichiara con la tilde (~) seguita dal nome della classe

Non ha accessori di visibilità

Non ha parametri

public class Car

{

~Car()

{

// cleanup code

}

}

Page 7: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

distruttori ~Car()

{

// cleanup code

}

protected override void Finalize() {

try {

// cleanup code

}

finally {

base.Finalize();

}

}

Trasformato dal compilatore

NotaIn questo modo il metodo Finalize viene richiamato in modo ricorsivoper tutte le istanze nella catena di ereditarietà, dalla più derivata alla meno derivata

Page 8: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Nota (I) Si consiglia di non utilizzare distruttori vuoti.

Quando una classe contiene un distruttore, viene creata una voce nella coda Finalize.

Quando il distruttore viene chiamato, il Garbage Collector viene richiamato per elaborare la coda.

Se il distruttore è vuoto, si verifica semplicemente un calo di prestazioni.

Page 9: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Nota (II) In c# non sono necessarie numerose attività di gestione della memoria

GC gestisce la memoria managed

Se si incapsulano risorse unmanaged (finestre, file, connessioni di rete, …) è necessario utilizzare i distruttori per liberare tali risorse

Quando l'oggetto può essere distrutto, il GC esegue il metodo Finalize dell'oggetto

Page 10: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Dispose Ok, il distruttore serve per rilasciare le risorse unmanaged,

ma se io ho bisogno di un rilascio deterministico di queste risorse?

C’è bisogno di un metodo da poter richiamare manualmente da codice

Questo metodo è il metodo Dispose() dell’interfaccia IDisposable

Page 11: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

IDisposable Definisce un metodo per rilasciare risorse allocate

Questa interfaccia viene utilizzata principalmente per rilasciare risorse non gestite

Quando si chiama una classe che implementa l'interfaccia IDisposable, utilizzare il modello try/finally

public interface IDisposable

{

void Dispose();

}

Page 12: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

try/finally Quando si utilizzano classi che implementano l’interfaccia IDisposable, bisogna sempre chiamare il metodo Dispose() non appena l’oggetto non è più utile

SqlConnection conn = null;

try{

conn = new SqlConnection(...);

...

}

finally

{

if (conn != null)

{

(conn as IDisposable).Dispose();

}

}

Page 13: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Osservazione Pattern “alloca-utilizza-rilascia”

Definisco il riferimento prima del try

Nel blocco try

Creo l’oggetto

Utilizzo l’oggetto

Nel blocco finally

Controllo che l’oggetto sia diverso da null

Eseguo un cast ad un oggetto IDisposable

Richiamo il metodo Dispose

Funziona alla perfezione ma il codice è molto prolisso

Page 14: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

using Lo statement using è stato creato apposta per eseguire il pattern

“alloca-utilizza-rilascia” in modo più conciso

SqlConnection conn = null;

try{

conn = new SqlConnection(...);

...

}

finally{

if (conn != null){

(conn as IDisposable).Dispose();

}

}

using (SqlConnection conn = new SqlConnection (...))

{

...

}

Dietro le quinte il compilatore c# trasforma il codice in alto nel codice a sinistra

Page 15: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Nota All'interno del blocco using, l'oggetto è di sola lettura e non può essere

modificato né riassegnato

Page 16: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Dispose e finalize 4 casi possibili

No Dispose, no Finalize

Si Dispose, no Finalize

Si Dispose, si Finalize

No Dispose, si Finalize

Page 17: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

No Dispose, No Finalize Il proprio oggetto

Utilizza la memoria

Utilizza altre risorse che non richiedono la deallocazione esplicita

Rilascia una risorsa unmanaged prima di uscire dal metodo che l’ha allocata

Questo è di gran lunga il caso più frequente

Page 18: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Si Dispose, No Finalize Il proprio oggetto

Alloca indirettamente risorse diverse dalla memoria attraverso altri oggetti .NET

Si vuole fornire ai client un metodo per rilasciare queste risorse prima possibile

È il secondo caso più frequente

class Sample : IDisposable

{

private bool disposed;

void IDisposable.Dispose(){

if (diposed) return;

disposed = true;

// rilascia le risorse

}

public void Method(){

if (disposed) throw new

ObjectDisposedException(...);

// codice del metodo

}

Page 19: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Si Dispose, Si Finalize Il proprio oggetto

Alloca direttamente una risorsa (tipicamente invocando un metodo di una DLL unmanaged) che richiede una deallocazione o rilascio esplicito

Questa deallocazione esplicita si fa nel metodo Finalize, ma si fornisce anche il metodo Dispose, per dare ai client la possibilità di rilasciare la risorsa prima della finalizzazione dei propri oggetti

Page 20: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

No Dispose, Si Finalize Non si ha alcuna risorsa da rilasciare, ma si ha necessità di eseguire una

determinata azione quando il proprio oggetto viene finalizzato

È il caso meno probabile e in pratica utile sono in alcuni casi particolari

Page 21: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Dispose e finalize 4 casi possibili

No Dispose, no Finalize

Si Dispose, no Finalize

Si Dispose, si Finalize

No Dispose, si Finalize

Page 22: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Primo approccio (I)public class ClipBoardWrapper : IDisposable

{

[DllImport("user32")]

private static extern int OpenClipboard(int hwnd);

[DllImport("user32")]

private static extern int CloseClipboard();

// Ricorda se la clipboard è correttamente aperta.

private bool isOpen;

// Apre la clipboard e la associa a una finestra.

public void Open(int hWnd)

{

// OpenClipboard restituisce 0 in caso d’errore

if(OpenClipboard(hWnd)

throw new Exception (“Unable to open the clipboard.”);

isOpen = true;

}

// continua...

Page 23: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Primo approccio (II)// Chiude la Clipboard – ignora il comando se non è aperta.

public void Close()

{

if (isOpen) CloseClipboard();

isOpen = false;

}

void IDisposable.Dispose()

{

Close();

}

~ClipBoardWrapper()

{

Close();

}

}

Page 24: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Problema 1 Se il GC chiama il metodo Finalize si ha una perdita di prestazioni

Bisogna fare in modo di richiamare il metodo Finalize solo quando è indispensabile

Non si deve chiamare quando è stato chiamato esplicitamente Dispose

Soluzione

Si richiama nel metodo Dispose GC.SuppressFinalize()

Page 25: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Problema 2 Il codice di rilascio potrebbe accedere ad altri oggetti referenziati

dall’oggetto corrente

Non si deve mai eseguire questo accesso se il codice di rilascio è in esecuzione nella fase di finalizzazione, poiché questi altri oggetti potrebbero essere già stati finalizzati

Soluzione

Si sposta il codice di rilascio effettiva in una versione in overload del metodo Dispose

Questo metodo accetta un argomento bool che specifica se l’oggetto è distrutto o finalizzato ed evita di accedere ad oggetti esterni nel secondo caso

Page 26: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Pattern Dispose-Finalize

Page 27: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Approccio semplificato Si avvolge ciascuna risorsa unmanaged che necessita della

finalizzazione con una classe il cui solo membro è un campo che contiene l’handle della risorsa unmanaged

Si nidifica questa classe wrapper all’interno di un’ulteriore classe che implementa il metodo dispose (ma non il metodo Finalize)

La classe privata viene contrassegnata come private

Page 28: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Demo

Page 29: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Caratteristiche UnmamagedResourceWrapper

Non deve contenere alcun campo eccetto l’handle

Se la risorsa unmanaged deve interagire con altre risorse, il codice deve essere posizionato in WinResource

WinResource

Deve coordinare tutte le risorse (managed e unmanaged) che ha allocato, e deve rilasciarle nel metodo Dispose

Page 30: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Vantaggi (I) Se il codice client omette di invocare il metodo WinResource.Dispose

La memoria utilizzata dall’oggetto WinResource verrà comunque azzerata alla prima garbage collection

L’oggetto interno ha il metodo Finalize e perciò verrà rilasciato solo durante la successiva garbage collection, ma questo oggetto consuma pochissima memoria e perciò non rappresenta un problema serio

UnmamagedResourceWrapper è privato e sealed

Non è necessario scrivere codice complesso che tenga conto delle classi derivate

Il codice all’esterno della classe WinResource non può ottenere un riferimento all’oggetto UnmamagedResourceWrapper e non può farlo risorgere

Page 31: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

Vantaggi (II) UnmamagedResourceWrapper ha un solo campo e questo campo è

di tipo value Il codice dei metodi Dispose o Finalize non può accedere erroneamente ad

alcun tipo di reference

Poiché c’è un solo handle di cui tenere conto, non è necessario scrivere del codice che si occupa degli errori nel costruttore

Se il costruttore fallisce il valore del campo handle continua ad essere uguale alla costante InvalidHandle

Il metodo Dispose può rilevare questa condizione e saltare il codice di rilascio

La classe UnmamagedResourceWrapper è così semplice e generica che spesso si può copiare e incollare il relativo codice all’interno di un’altra classe

Page 32: Alessandro Andreose’ - Helpdesk Website Studio e... · Il metodo finalize (Distruttore) Metodo speciale Il garbage collector invoca questo metodo prima di rilasciare la memoria

using e Dispose