www.devleap.itwww.devleap.it
Gestione della memoriaGestione della memoriae delle risorse in .NET e delle risorse in .NET
Marco RussoMarco RussoMCSD MCAD MCSE+I MCSA MCDBA MCTMCSD MCAD MCSE+I MCSA MCDBA MCT
Mail: [email protected]: [email protected] blog: http://blogs.devleap.com/marco.blogItalian blog: http://blogs.devleap.com/marco.blog
Iscriviti suIscriviti suwww.microsoft.com/italy/technicalconference/default.mspxwww.microsoft.com/italy/technicalconference/default.mspx
Gestione e infrastruttura di reteGestione e infrastruttura di rete Smart Client: la potenza del PC e la connettività WebSmart Client: la potenza del PC e la connettività Web Gestione dell'infrastruttura dei sistemi di produttività aziendaleGestione dell'infrastruttura dei sistemi di produttività aziendale Sicurezza e interoperabilitàSicurezza e interoperabilità
Guest Speaker
Bill GatesNon perdere l'appuntamento dedicato ai professionisti IT e agli sviluppatori
www.devleap.it
DevLeap: chi siamoDevLeap: chi siamo• www.DevLeap.itwww.DevLeap.it• Un gruppo di 5 persone con tanta voglia Un gruppo di 5 persone con tanta voglia
didi• Studiare a fondo le tecnologieStudiare a fondo le tecnologie• Capire il “behind the scenes”Capire il “behind the scenes”• Implementare soluzioni realiImplementare soluzioni reali• Confrontarsi con le problematiche realiConfrontarsi con le problematiche reali• Sperimentare nuove ideeSperimentare nuove idee
• Facciamo Corsi, Conferenze, TrainingFacciamo Corsi, Conferenze, Training• Scriviamo libriScriviamo libri
• http://www.devleap.it/fullcontacthttp://www.devleap.it/fullcontact
www.devleap.it
Chi siete ?Chi siete ?• Chi usa già .NET?Chi usa già .NET?• Chi usa C#?Chi usa C#?• Chi usa VB.NET?Chi usa VB.NET?• Chi usa C++/Managed C++?Chi usa C++/Managed C++?• Chi conosce Java?Chi conosce Java?
www.devleap.it
AgendaAgenda• Perché il Garbage CollectorPerché il Garbage Collector• Algoritmo mark-and-compactAlgoritmo mark-and-compact• F-reachable queueF-reachable queue• Pattern IDisposable Pattern IDisposable • ResurrectionResurrection• Weak ReferenceWeak Reference• Algoritmo generazionaleAlgoritmo generazionale• Thread e Garbage CollectorThread e Garbage Collector• Garbage Collector vs. Heap Win32Garbage Collector vs. Heap Win32
www.devleap.it
Perché il Garbage CollectorPerché il Garbage Collector• La gestione della memoria è La gestione della memoria è
fondamentale nei linguaggi a oggettifondamentale nei linguaggi a oggetti• I modelli tradizionali sono I modelli tradizionali sono
deterministici:deterministici:• Distruzione manuale (C, C++, Win32)Distruzione manuale (C, C++, Win32)• Reference counting (COM, VB)Reference counting (COM, VB)• Minore uso di memoriaMinore uso di memoria• Maggiore consumo di CPUMaggiore consumo di CPU
• Modelli alternativi (euristici):Modelli alternativi (euristici):• Mark/sweepMark/sweep• Copy collectCopy collect• Mark/compactMark/compact
www.devleap.it
Algoritmi di Garbage CollectionAlgoritmi di Garbage Collection
• Mark/SweepMark/Sweep• Segna tutti gli oggetti “raggiungibili”Segna tutti gli oggetti “raggiungibili”• Gli oggetti non raggiungibili vengono Gli oggetti non raggiungibili vengono
distruttidistrutti• Crea frammentazione memoria liberaCrea frammentazione memoria libera
• Copy CollectCopy Collect• Copia tutti gli oggetti raggiungibili in una Copia tutti gli oggetti raggiungibili in una
nuova zona di memoria (contigua)nuova zona di memoria (contigua)• Aggiorna tutti i riferimenti agli oggetti Aggiorna tutti i riferimenti agli oggetti
copiaticopiati• Libera la zona di memoria esaminataLibera la zona di memoria esaminata
• Mark/CompactMark/Compact• Soluzione ibrida, implementata da .NETSoluzione ibrida, implementata da .NET
www.devleap.it
Algoritmo mark-and-compactAlgoritmo mark-and-compact
• Allocazione sequenziale di tutti gli oggettiAllocazione sequenziale di tutti gli oggetti• Alte prestazioniAlte prestazioni
a.ba.b a2.ba2.baa aa a2a2 bb
A a = new A();A a = new A();
a.b = new B();a.b = new B();
A a2 = new A();A a2 = new A();
a2.b = new B();a2.b = new B();
NextObjPtrNextObjPtr
www.devleap.it
Algoritmo mark-and-compactAlgoritmo mark-and-compact
• L’operazione di Garbage Collection L’operazione di Garbage Collection raccoglie gli oggetti non raggiungibiliraccoglie gli oggetti non raggiungibili
• Avviene automaticamente quando Avviene automaticamente quando l’heap crescel’heap cresce
• Pilotata manualmente da Pilotata manualmente da GC.Collect()GC.Collect()• Due fasi:Due fasi:
1.1. MarkMark
individua la memoria non raggiungibileindividua la memoria non raggiungibile
2.2. CompactCompact
sposta tutti gli oggetti all’inizio dell’heap, sposta tutti gli oggetti all’inizio dell’heap, aggiornando tutti i riferimentiaggiornando tutti i riferimenti
www.devleap.it
GC fase 1: MarkGC fase 1: Mark
• Identifica gli oggetti referenziati, Identifica gli oggetti referenziati, raggiungibili da posti conosciuti (raggiungibili da posti conosciuti (root root setset):):• Proprietà AppDomainProprietà AppDomain• Registri CPURegistri CPU• Slot TLSSlot TLS• Variabili locali sullo stackVariabili locali sullo stack• Membri statici delle classiMembri statici delle classi
• Iterazione ricorsiva degli oggetti Iterazione ricorsiva degli oggetti referenziati per “segnare” tutti gli referenziati per “segnare” tutti gli oggetti raggiungibilioggetti raggiungibili
www.devleap.it
GC fase 2: CompactGC fase 2: Compact
• Tutti gli oggetti “raggiungibili” vengono Tutti gli oggetti “raggiungibili” vengono spostati all’inizio dell’heapspostati all’inizio dell’heap
• Tutti i riferimenti vengono aggiornatiTutti i riferimenti vengono aggiornati• Lo spazio libero non resta frammentatoLo spazio libero non resta frammentato
www.devleap.it
GC fase 1: MarkGC fase 1: Mark
NextObjPtrNextObjPtr
Oggetti “vivi”Oggetti “vivi”
Oggetti non raggiungibiliOggetti non raggiungibili
Spazio liberoSpazio libero
Root setRoot set
www.devleap.it
GC fase 2: CompactGC fase 2: Compact
NextObjPtrNextObjPtr
Oggetti “vivi”Oggetti “vivi”
Spazio liberoSpazio libero
Root setRoot set
Spazio recuperatoSpazio recuperato
www.devleap.it
Finalization queueFinalization queue
• Gli oggetti che possiedono un finalizer Gli oggetti che possiedono un finalizer non possono essere subito raccolti dal non possono essere subito raccolti dal GCGC
• In C# il distruttore è sinonimo di FinalizeIn C# il distruttore è sinonimo di Finalize• Chiamata del finalizer dopo collectionChiamata del finalizer dopo collection• Alla creazione di un oggetto che possiede Alla creazione di un oggetto che possiede
Finalize, viene aggiunto un riferimento Finalize, viene aggiunto un riferimento all’oggetto nella Finalization queueall’oggetto nella Finalization queue
• Durante il GC, gli oggetti referenziati Durante il GC, gli oggetti referenziati solo da Finalization queue finiscono in solo da Finalization queue finiscono in F-reachable queueF-reachable queue
www.devleap.it
F-reachable queueF-reachable queue
• F-reachable queue contiene riferimento F-reachable queue contiene riferimento a oggetti con finalizer da richiamarea oggetti con finalizer da richiamare
• Alimentata da Finalization queueAlimentata da Finalization queue• I riferimenti contenuti da Finalization eI riferimenti contenuti da Finalization e
F-reachable queue diventano parte del root F-reachable queue diventano parte del root setset
www.devleap.it
F-reachable queueF-reachable queue
Oggetti “vivi”Oggetti “vivi”
In attesa di FinalizeIn attesa di Finalize
Spazio liberoSpazio libero
Root setRoot set
F-reachable queueF-reachable queue
www.devleap.it
F-reachable queueF-reachable queue
• F-reachable queue viene smaltita in un F-reachable queue viene smaltita in un thread dedicato che agisce in thread dedicato che agisce in backgroundbackground
• GC.WaitForPendingFinalizer() per GC.WaitForPendingFinalizer() per attendere che F-reachable queue sia attendere che F-reachable queue sia vuotavuota
• Ordine di chiamata su Finalize() non Ordine di chiamata su Finalize() non segue eventuali gerarchie padre-figliosegue eventuali gerarchie padre-figlio• Per queste esigenze usare Dispose()Per queste esigenze usare Dispose()
• Evitare di implementare Finalize se non Evitare di implementare Finalize se non è necessario (migliori prestazioni)è necessario (migliori prestazioni)
www.devleap.it
Pattern IDisposablePattern IDisposable
• In alcuni casi serve un comportamento In alcuni casi serve un comportamento di finalizzazione deterministica:di finalizzazione deterministica:• Riferimenti a oggetti non gestitiRiferimenti a oggetti non gestiti• Utilizzo di risorse che devono essere Utilizzo di risorse che devono essere
rilasciate appena termina il loro utilizzorilasciate appena termina il loro utilizzo
• Non si possono usare i finalizzatori, che Non si possono usare i finalizzatori, che non sono richiamabili direttamentenon sono richiamabili direttamente
• Implementare l’interfaccia IDisposableImplementare l’interfaccia IDisposable
www.devleap.it
Pattern IDisposable (1)Pattern IDisposable (1)class DisposeDemo : BaseClass, IDisposable {class DisposeDemo : BaseClass, IDisposable { OtherRes otherRes;OtherRes otherRes; private disposed = false;private disposed = false;
private void freeState {private void freeState { if (!disposed) { // Evita doppia esecuzioneif (!disposed) { // Evita doppia esecuzione // Chiude risorse allocate (es. handle non gestiti)// Chiude risorse allocate (es. handle non gestiti) disposed = true;disposed = true; }} }} public void public void DisposeDispose() {() { freeState();freeState(); otherRes.Dispose();// Dispose oggetti membrootherRes.Dispose();// Dispose oggetti membro base.Dispose(); // Dispose classe base (BaseClass)base.Dispose(); // Dispose classe base (BaseClass) GC.SuppressFinalizeGC.SuppressFinalize( this );( this ); }} ~DisposeDemo() {~DisposeDemo() { freeState();freeState(); }}}}
www.devleap.it
Pattern IDisposable (2 a)Pattern IDisposable (2 a)class BaseResource : IDisposable {class BaseResource : IDisposable { OtherRes otherRes;OtherRes otherRes; private disposed = false;private disposed = false;
protected protected virtualvirtual void Dispose( bool disposing ) { void Dispose( bool disposing ) { if (!this.disposed) {if (!this.disposed) { if (disposing) {if (disposing) { otherRes.Dispose(); // Dispose oggetti membrootherRes.Dispose(); // Dispose oggetti membro }} CloseHandle( ... ); // Chiude risorse non gestiteCloseHandle( ... ); // Chiude risorse non gestite this.disposed = true;this.disposed = true; }} }} public void public void DisposeDispose() {() { Dispose( true );Dispose( true ); GC.SuppressFinalizeGC.SuppressFinalize( this );( this ); }} ~DisposeDemo() {~DisposeDemo() { Dispose( false );Dispose( false ); }}}}
www.devleap.it
Pattern IDisposable (2 b)Pattern IDisposable (2 b)class MyResource : BaseResource { // Implementa già IDisposableclass MyResource : BaseResource { // Implementa già IDisposable private disposed = false;private disposed = false;
protected protected overrideoverride void Dispose( bool disposing ) { void Dispose( bool disposing ) { if (!this.disposed) {if (!this.disposed) { if (disposing) {if (disposing) { // Dispose altri oggetti gestiti// Dispose altri oggetti gestiti }} // Chiude risorse non gestite// Chiude risorse non gestite this.disposed = true;this.disposed = true; }} base.Dispose();base.Dispose(); }}
// La funzione // La funzione Dispose() Dispose() è già implementata nella classe baseè già implementata nella classe base
// Il // Il finalizzatorefinalizzatore che chiama Dispose(bool) virtuale è già che chiama Dispose(bool) virtuale è già // implementato nella classe base// implementato nella classe base}}
www.devleap.it
Componenti .NETComponenti .NET• System.System.ObjectObject
• Non implementa nessuna interfacciaNon implementa nessuna interfaccia
• System.ComponentModel.System.ComponentModel.ComponentComponent• Implementa IDisposable col pattern (2)Implementa IDisposable col pattern (2)• Deriva da MarshalByRefObjectDeriva da MarshalByRefObject
• System.Windows.Forms.System.Windows.Forms.ControlControl• Deriva da Deriva da
System.ComponentModel.ComponentSystem.ComponentModel.Component• Controlli “progettabili” graficamenteControlli “progettabili” graficamente• Per controlli interattivi derivare Per controlli interattivi derivare UserControlUserControl
• System.Web.UI.System.Web.UI.ControlControl• Implementa IDisposable col pattern (1)Implementa IDisposable col pattern (1)
www.devleap.it
ResurrectionResurrection
• Chiamare GC.SuppressFinalize() per Chiamare GC.SuppressFinalize() per eliminare l’oggetto da F-reachable eliminare l’oggetto da F-reachable queuequeue• Finalize() non verrà più richiamataFinalize() non verrà più richiamata
• Un oggetto può ri-registrare il Un oggetto può ri-registrare il finalizzatore chiamando finalizzatore chiamando GC.ReRegisterForFinalize()GC.ReRegisterForFinalize()• Tecnica chiamata “resurrection”Tecnica chiamata “resurrection”• In Finalize() si assegna In Finalize() si assegna thisthis ad un oggetto ad un oggetto
raggiungibile da root setraggiungibile da root set• GC.ReRegisterForFinalize() garantisce la GC.ReRegisterForFinalize() garantisce la
successiva ri-esecuzione di Finalize()successiva ri-esecuzione di Finalize()• Da non usareDa non usare se non si è più che sicuri !! se non si è più che sicuri !!
www.devleap.it
ResurrectionResurrection
public class BaseObj {public class BaseObj { // ...// ... ~BaseObj() {~BaseObj() { Application.ObjHolder = this;Application.ObjHolder = this; GC.ReRegisterForFinalize( this );GC.ReRegisterForFinalize( this ); }} // ...// ...}}
class Application {class Application { static public Object ObjHandler;static public Object ObjHandler; // ...// ...}}
www.devleap.it
Weak ReferenceWeak Reference
• Un Weak Reference è un riferimento Un Weak Reference è un riferimento “debole” ad un oggetto“debole” ad un oggetto
• Non viene considerato come riferimento Non viene considerato come riferimento da parte del Garbage Collectorda parte del Garbage Collector
• Se l’oggetto viene raccolto dal GC, il Se l’oggetto viene raccolto dal GC, il Weak Reference vale nullWeak Reference vale null
• Utile per meccanismi di cacheUtile per meccanismi di cache• Esistono due tipi di Weak Reference:Esistono due tipi di Weak Reference:
• Short Weak ReferenceShort Weak Reference• Long Weak ReferenceLong Weak Reference
www.devleap.it
Weak ReferenceWeak Reference
• Short Weak ReferenceShort Weak Reference• Valido fino a che l’oggetto non viene Valido fino a che l’oggetto non viene
finalizzatofinalizzato
• Long Weak ReferenceLong Weak Reference• Valido anche dopo la finalizzazioneValido anche dopo la finalizzazione• Utilizzabile per Resurrection - consente di Utilizzabile per Resurrection - consente di
chiamare GC.ReRegisterForFinalize()chiamare GC.ReRegisterForFinalize()
WeakReference( Object target );WeakReference( Object target ); WeakReference( Object target, Boolean trackResurrection );WeakReference( Object target, Boolean trackResurrection );
www.devleap.it
Algoritmo generazionaleAlgoritmo generazionale
• Ottimizzazione dell’algoritmo di GCOttimizzazione dell’algoritmo di GC• Statisticamente gli oggetti più giovani Statisticamente gli oggetti più giovani
hanno una vita più brevehanno una vita più breve• Il GC definisce l’età in base alla durata della Il GC definisce l’età in base alla durata della
presenza sull’heappresenza sull’heap
• Generazione: insieme di elementi che Generazione: insieme di elementi che sopravvivono ad un GCsopravvivono ad un GC
• Le generazioni più vecchie vengono Le generazioni più vecchie vengono analizzate dal GC meno sovente analizzate dal GC meno sovente
www.devleap.it
Algoritmo generazionaleAlgoritmo generazionale
• Ad ogni GC gli oggetti che sopravvivono Ad ogni GC gli oggetti che sopravvivono vengono promossi di una generazionevengono promossi di una generazione
• L’implementazione attuale definisce 3 L’implementazione attuale definisce 3 generazionigenerazioni
G2G2Oggetti Oggetti sopravvissuti sopravvissuti a due o più a due o più GCGC
G1G1Oggetti Oggetti sopravvissusopravvissuti a un GCti a un GC
G0G0Oggetti mai Oggetti mai sottoposti a sottoposti a GCGC
www.devleap.it
Algoritmo generazionaleAlgoritmo generazionale
• GC.GetGeneration()GC.GetGeneration() restituisce la restituisce la generazione di un oggettogenerazione di un oggetto
• Frequenza GC su generazioni più Frequenza GC su generazioni più vecchie fino a 1/10 di quelle recentivecchie fino a 1/10 di quelle recenti
• Il GC non segue i riferimenti che Il GC non segue i riferimenti che puntano a generazioni più vecchiepuntano a generazioni più vecchie
• GC.Collect( n )GC.Collect( n ) consente di specificare consente di specificare quante generazioni esaminarequante generazioni esaminare
• Gli oggetti con Finalize() vengono Gli oggetti con Finalize() vengono promossi almeno una voltapromossi almeno una volta
www.devleap.it
Large Object HeapLarge Object Heap
• Heap separato per oggetti di grandi Heap separato per oggetti di grandi dimensioni (>20k)dimensioni (>20k)• Il loro spostamento è costosoIl loro spostamento è costoso• Di solito non sono frequentiDi solito non sono frequenti• Gli oggetti non vengono spostatiGli oggetti non vengono spostati• Simile a un tradizionale heap C/C++Simile a un tradizionale heap C/C++
www.devleap.it
Thread e Garbage CollectorThread e Garbage Collector• Versioni di GC diverse per mono e Versioni di GC diverse per mono e
multi-processoremulti-processore• Ogni processo ha un thread separato Ogni processo ha un thread separato
per chiamare i Finalize() degli oggettiper chiamare i Finalize() degli oggetti• Priorità più alta non real-timePriorità più alta non real-time• I metodi Finalize() vengono I metodi Finalize() vengono sempresempre
chiamati da questo threadchiamati da questo thread
• L’operazione di GC arresta tutti i L’operazione di GC arresta tutti i thread gestiti thread gestiti (root set deve essere noto)(root set deve essere noto)• Thread hijackingThread hijacking• Safe pointsSafe points• Fully interruptible codeFully interruptible code
www.devleap.it
Thread hijackingThread hijacking
• In presenza di un metodo breve, In presenza di un metodo breve, l’indirizzo di ritorno viene deviato verso il l’indirizzo di ritorno viene deviato verso il GC, che ritorna poi al percorso originaleGC, che ritorna poi al percorso originale
int bar() int bar() {{ return 42;return 42;}}
int foo() int foo() {{ int ret = bar();int ret = bar(); return ret;return ret;}}
GCGC
www.devleap.it
Safe PointsSafe Points
• In metodi lunghi il jitter inserisce delle In metodi lunghi il jitter inserisce delle chiamate che sospendono il thread se chiamate che sospendono il thread se deve essere eseguito il GCdeve essere eseguito il GC
int bar() int bar() {{ ...... ...... ...... ...... ...... ...... ...... ......}}
GCGCSe GC in Se GC in attesa, attesa, sospende il sospende il thread fino thread fino a che il GC a che il GC non viene non viene completatocompletato
GC Safe PointGC Safe Point
www.devleap.it
Fully interruptible codeFully interruptible code
• In loop stretti il thread viene sospeso ed il GC In loop stretti il thread viene sospeso ed il GC esamina una tabella generata dal jitter con gli esamina una tabella generata dal jitter con gli oggetti attivi (root set) ad ogni lineaoggetti attivi (root set) ad ogni linea
......08 while (condition) {08 while (condition) {09 Foo a = new Foo();09 Foo a = new Foo();10 Bar b = new Bar();10 Bar b = new Bar();11 Foo c = new Foo();11 Foo c = new Foo();121213 a.DoSomething();13 a.DoSomething();14 condition = c.Good();14 condition = c.Good();15 }15 } ......
LineaLinea OggettiOggetti
0909 {}{}
1010 {a}{a}
1111 {a,c}{a,c}
1212 {a,c}{a,c}
1313 {c}{c}
1414 {}{}
www.devleap.it
Concurrent Garbage CollectorConcurrent Garbage Collector
• Configurabile a livello di host CLRConfigurabile a livello di host CLR• Parametro flags di Parametro flags di CorBindToRuntimeEx()CorBindToRuntimeEx()
• ConcurrentGCConcurrentGC• Non rallenta interfaccia utenteNon rallenta interfaccia utente• Prestazioni complessive più bassePrestazioni complessive più basse• Costruzione del grafo di raggiungibilità in Costruzione del grafo di raggiungibilità in
backgroundbackground
• Non-concurrent GCNon-concurrent GC• Agisce nello stesso thread del codice utenteAgisce nello stesso thread del codice utente• Migliori prestazioni complessiveMigliori prestazioni complessive• Indicato per applicazioni serverIndicato per applicazioni server
• Default in file configurazione runtime:Default in file configurazione runtime:<configuration> <runtime> <<configuration> <runtime> <gcConcurrentgcConcurrent enabled="false"/> enabled="false"/> </runtime> </configuration></runtime> </configuration>
www.devleap.it
Garbage Collector vs. Heap Win32Garbage Collector vs. Heap Win32
• Allocazione più veloceAllocazione più veloce• Penalizzazione se l’oggetto ha FinalizePenalizzazione se l’oggetto ha Finalize
• Anche l’allocazione è più lentaAnche l’allocazione è più lenta
• Importante chiamare Importante chiamare GC.SuppressFinalize() su Dispose()GC.SuppressFinalize() su Dispose()
• Heap C++ per build Debug Heap C++ per build Debug estremamente più lento (anche 1.000 estremamente più lento (anche 1.000 volte)volte)
• Paragone difficileParagone difficile• Il finalizzatore può essere richiamato in Il finalizzatore può essere richiamato in
background, mentre il distruttore è sempre background, mentre il distruttore è sempre sincrono nel thread chiamantesincrono nel thread chiamante
www.devleap.it
Considerazioni finaliConsiderazioni finali
• Non interagire direttamente con GC se Non interagire direttamente con GC se non c’è un buon motivonon c’è un buon motivo• Tentare di risolvere un bug chiamando Tentare di risolvere un bug chiamando
GC.Collect() non è un buon motivo!GC.Collect() non è un buon motivo!
• Evitare finalizzatori se le prestazioniEvitare finalizzatori se le prestazionisono importantisono importanti• Va più veloce della gestione Heap Va più veloce della gestione Heap
tradizionaletradizionale
• Weak reference e Resurrection solo se Weak reference e Resurrection solo se c’è un ottimo motivoc’è un ottimo motivo• Evitare di creare più problemi di quantiEvitare di creare più problemi di quanti
se ne risolvonose ne risolvono
www.devleap.it
Riferimenti utiliRiferimenti utili
• Demo di tutte le funzionalità di GC:Demo di tutte le funzionalità di GC:C:\Program Files\Microsoft.NET\FrameworkSDK\Samples\C:\Program Files\Microsoft.NET\FrameworkSDK\Samples\technologies\GarbageCollectiontechnologies\GarbageCollection
• Articoli di Jeffrey RichterArticoli di Jeffrey Richtersu MSDN Magazine:su MSDN Magazine:http://msdn.microsoft.com/library/default.asp?url=/library/en-http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI.aspus/dnmag00/html/GCI.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI2.aspus/dnmag00/html/GCI2.asp
www.devleap.it
Altre InformazioniAltre Informazioni• Dove posso ottenere maggiori Dove posso ottenere maggiori
informazioniinformazioni• www.devleap.itwww.devleap.it• www.microsoft.com/msdn/italywww.microsoft.com/msdn/italy• msdn.microsoft.commsdn.microsoft.com
• Developer resourcesDeveloper resources• Microsoft Visual Studio.NETMicrosoft Visual Studio.NET• Microsoft .NET Framework SDKMicrosoft .NET Framework SDK• Microsoft Developer NetworkMicrosoft Developer Network
www.devleap.it
Gestione della memoriaGestione della memoriae delle risorse in .NET e delle risorse in .NET
I vostri feedback sono importantiI vostri feedback sono importanti•ScriveteciScriveteci
Grazie della partecipazione– A presto
Top Related