Gestione della memoria e delle risorse in .NET  Marco Russo MCSD MCAD MCSE+I MCSA MCDBA MCT Mail: marco@devleap.it Italian blog: http://blogs.devleap.com/marco.blog
Iscriviti su www.microsoft.com/italy/technicalconference/default.mspx Gestione e infrastruttura di rete Smart Client: la potenza del PC e la connettività Web Gestione dell'infrastruttura dei sistemi di produttività aziendale Sicurezza e interoperabilità Guest Speaker Bill Gates Non perdere l'appuntamento dedicato  ai professionisti IT e agli sviluppatori
DevLeap: chi siamo www.DevLeap.it Un gruppo di 5 persone con tanta voglia di Studiare a fondo le tecnologie Capire il “behind the scenes” Implementare soluzioni reali Confrontarsi con le problematiche reali Sperimentare nuove idee Facciamo Corsi, Conferenze, Training Scriviamo libri http://www.devleap.it/fullcontact
Chi siete ? Chi usa già .NET? Chi usa C#? Chi usa VB.NET? Chi usa C++/Managed C++? Chi conosce Java?
Agenda Perché il Garbage Collector Algoritmo mark-and-compact F-reachable queue Pattern IDisposable  Resurrection Weak Reference Algoritmo generazionale Thread e Garbage Collector Garbage Collector vs. Heap Win32
Perché il Garbage Collector La gestione della memoria è fondamentale nei linguaggi a oggetti I modelli tradizionali sono deterministici: Distruzione manuale (C, C++, Win32) Reference counting (COM, VB) Minore uso di memoria Maggiore consumo di CPU Modelli alternativi (euristici): Mark/sweep Copy collect Mark/compact
Algoritmi di Garbage Collection Mark/Sweep Segna tutti gli oggetti “raggiungibili” Gli oggetti non raggiungibili vengono distrutti Crea frammentazione memoria libera Copy Collect Copia tutti gli oggetti raggiungibili in una nuova zona di memoria (contigua) Aggiorna tutti i riferimenti agli oggetti copiati Libera la zona di memoria esaminata Mark/Compact Soluzione ibrida, implementata da .NET
Algoritmo mark-and-compact Allocazione sequenziale di tutti gli oggetti Alte prestazioni a.b a2.b A a = new A(); a.b = new B(); A a2 = new A(); a2.b = new B(); NextObjPtr a a a2 b
Algoritmo mark-and-compact L’operazione di Garbage Collection raccoglie gli oggetti non raggiungibili Avviene automaticamente quando l’heap cresce Pilotata manualmente da  GC.Collect() Due fasi: Mark individua la memoria non raggiungibile Compact sposta tutti gli oggetti all’inizio dell’heap, aggiornando tutti i riferimenti
GC fase 1: Mark Identifica gli oggetti referenziati, raggiungibili da posti conosciuti ( root set ): Proprietà AppDomain Registri CPU Slot TLS Variabili locali sullo stack Membri statici delle classi Iterazione ricorsiva degli oggetti referenziati per “segnare” tutti gli oggetti raggiungibili
GC fase 2: Compact Tutti gli oggetti “raggiungibili” vengono spostati all’inizio dell’heap Tutti i riferimenti vengono aggiornati Lo spazio libero non resta frammentato
GC fase 1: Mark NextObjPtr Oggetti “vivi” Oggetti non raggiungibili Spazio libero Root set
GC fase 2: Compact NextObjPtr Oggetti “vivi” Spazio libero Root set Spazio recuperato
Finalization queue Gli oggetti che possiedono un finalizer non possono essere subito raccolti dal GC In C# il distruttore è sinonimo di Finalize Chiamata del finalizer dopo collection Alla creazione di un oggetto che possiede Finalize, viene aggiunto un riferimento all’oggetto nella Finalization queue Durante il GC, gli oggetti referenziati solo da Finalization queue finiscono in  F-reachable queue
F-reachable queue F-reachable queue contiene riferimento a oggetti con finalizer da richiamare Alimentata da Finalization queue I riferimenti contenuti da Finalization e F-reachable queue diventano parte del root set
F-reachable queue Oggetti “vivi” In attesa di Finalize Spazio libero Root set F-reachable queue
F-reachable queue F-reachable queue viene smaltita in un thread dedicato che agisce in background GC.WaitForPendingFinalizer() per attendere che F-reachable queue sia vuota Ordine di chiamata su Finalize() non segue eventuali gerarchie padre-figlio Per queste esigenze usare Dispose() Evitare di implementare Finalize se non è necessario (migliori prestazioni)
Pattern IDisposable In alcuni casi serve un comportamento di finalizzazione deterministica: Riferimenti a oggetti non gestiti Utilizzo di risorse che devono essere rilasciate appena termina il loro utilizzo Non si possono usare i finalizzatori, che non sono richiamabili direttamente Implementare l’interfaccia IDisposable
Pattern IDisposable (1) class DisposeDemo : BaseClass, IDisposable { OtherRes  otherRes; private  disposed = false; private void freeState { if (!disposed) {  // Evita doppia esecuzione // Chiude risorse allocate (es. handle non gestiti) disposed = true; } } public void  Dispose () { freeState(); otherRes.Dispose();// Dispose oggetti membro base.Dispose();  // Dispose classe base (BaseClass) GC.SuppressFinalize ( this ); } ~DisposeDemo() { freeState(); } }
Pattern IDisposable (2 a) class BaseResource : IDisposable { OtherRes  otherRes; private  disposed = false; protected  virtual  void Dispose( bool disposing ) { if (!this.disposed) { if (disposing) { otherRes.Dispose(); // Dispose oggetti membro } CloseHandle( ... );  // Chiude risorse non gestite this.disposed = true; } } public void  Dispose () { Dispose( true ); GC.SuppressFinalize ( this ); } ~DisposeDemo() { Dispose( false ); } }
Pattern IDisposable (2 b) class MyResource : BaseResource {  // Implementa già IDisposable private  disposed = false; protected  override  void Dispose( bool disposing ) { if (!this.disposed) { if (disposing) { // Dispose altri oggetti gestiti } // Chiude risorse non gestite this.disposed = true; } base.Dispose(); } // La funzione  Dispose()  è già implementata nella classe base // Il  finalizzatore  che chiama Dispose(bool) virtuale è già // implementato nella classe base }
Componenti .NET System. Object Non implementa nessuna interfaccia System.ComponentModel. Component Implementa IDisposable col pattern (2) Deriva da MarshalByRefObject System.Windows.Forms. Control Deriva da System.ComponentModel.Component Controlli “progettabili” graficamente Per controlli interattivi derivare  UserControl System.Web.UI. Control Implementa IDisposable col pattern (1)
Resurrection Chiamare GC.SuppressFinalize() per eliminare l’oggetto da F-reachable queue Finalize() non verrà più richiamata Un oggetto può ri-registrare il finalizzatore chiamando GC.ReRegisterForFinalize() Tecnica chiamata “resurrection” In Finalize() si assegna  this  ad un oggetto raggiungibile da root set GC.ReRegisterForFinalize() garantisce la successiva ri-esecuzione di Finalize() Da non usare  se non si è più che sicuri !!
Resurrection public class BaseObj { // ... ~BaseObj() { Application.ObjHolder = this; GC.ReRegisterForFinalize( this ); } // ... } class Application { static public Object ObjHandler; // ... }
Weak Reference Un Weak Reference è un riferimento “debole” ad un oggetto Non viene considerato come riferimento da parte del Garbage Collector Se l’oggetto viene raccolto dal GC, il Weak Reference vale null Utile per meccanismi di cache Esistono due tipi di Weak Reference: Short Weak Reference Long Weak Reference
Weak Reference Short Weak Reference Valido fino a che l’oggetto non viene finalizzato Long Weak Reference Valido anche dopo la finalizzazione Utilizzabile per Resurrection - consente di chiamare GC.ReRegisterForFinalize() WeakReference( Object target ); WeakReference( Object target, Boolean trackResurrection );
Algoritmo generazionale Ottimizzazione dell’algoritmo di GC Statisticamente gli oggetti più giovani hanno una vita più breve Il GC definisce l’età in base alla durata della presenza sull’heap Generazione: insieme di elementi che sopravvivono ad un GC Le generazioni più vecchie vengono analizzate dal GC meno sovente
Algoritmo generazionale Ad ogni GC gli oggetti che sopravvivono vengono promossi di una generazione L’implementazione attuale definisce 3 generazioni G2 Oggetti sopravvissuti a due o più GC G1 Oggetti sopravvissuti a un GC G0 Oggetti mai sottoposti a GC
Algoritmo generazionale GC.GetGeneration()  restituisce la generazione di un oggetto Frequenza GC su generazioni più vecchie fino a 1/10 di quelle recenti Il GC non segue i riferimenti che puntano a generazioni più vecchie GC.Collect( n )  consente di specificare quante generazioni esaminare Gli oggetti con Finalize() vengono promossi almeno una volta
Large Object Heap Heap separato per oggetti di grandi dimensioni (>20k) Il loro spostamento è costoso Di solito non sono frequenti Gli oggetti non vengono spostati Simile a un tradizionale heap C/C++
Thread e Garbage Collector Versioni di GC diverse per mono e multi-processore Ogni processo ha un thread separato per chiamare i Finalize() degli oggetti Priorità più alta non real-time I metodi Finalize() vengono  sempre  chiamati da questo thread L’operazione di GC arresta tutti i thread gestiti  (root set deve essere noto) Thread hijacking Safe points Fully interruptible code
Thread hijacking In presenza di un metodo breve, l’indirizzo di ritorno viene deviato verso il GC, che ritorna poi al percorso originale int bar()  { return 42; } int foo()  { int ret = bar(); return ret; } GC
Safe Points In metodi lunghi il jitter inserisce delle chiamate che sospendono il thread se deve essere eseguito il GC int bar()  { ... ... ... ... ... ... ... ... } GC Se GC in attesa, sospende il thread fino a che il GC non viene completato GC Safe Point
Fully interruptible code In loop stretti il thread viene sospeso ed il GC esamina una tabella generata dal jitter con gli oggetti attivi (root set) ad ogni linea ... 08  while (condition) { 09  Foo a = new Foo(); 10  Bar b = new Bar(); 11  Foo c = new Foo(); 12 13  a.DoSomething(); 14  condition = c.Good(); 15  } ... {} 14 {c} 13 {a,c} 12 {a,c} 11 {a} 10 {} 09 Oggetti Linea
Concurrent Garbage Collector Configurabile a livello di host CLR Parametro flags di  CorBindToRuntimeEx() ConcurrentGC Non rallenta interfaccia utente Prestazioni complessive più basse Costruzione del grafo di raggiungibilità in background Non-concurrent GC Agisce nello stesso thread del codice utente Migliori prestazioni complessive Indicato per applicazioni server Default in file configurazione runtime: <configuration> <runtime> < gcConcurrent  enabled=&quot;false&quot;/> </runtime> </configuration>
Garbage Collector vs. Heap Win32 Allocazione più veloce Penalizzazione se l’oggetto ha Finalize Anche l’allocazione è più lenta Importante chiamare GC.SuppressFinalize() su Dispose() Heap C++ per build Debug estremamente più lento (anche 1.000 volte) Paragone difficile Il finalizzatore può essere richiamato in background, mentre il distruttore è sempre sincrono nel thread chiamante
Considerazioni finali Non interagire direttamente con GC se non c’è un buon motivo Tentare di risolvere un bug chiamando GC.Collect() non è un buon motivo! Evitare finalizzatori se le prestazioni sono importanti Va più veloce della gestione Heap tradizionale Weak reference e Resurrection solo se c’è un ottimo motivo Evitare di creare più problemi di quanti se ne risolvono
Riferimenti utili Demo di tutte le funzionalità di GC: C:\Program Files\Microsoft.NET\FrameworkSDK\Samples\ technologies\GarbageCollection Articoli di Jeffrey Richter su MSDN Magazine: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI.asp http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI2.asp
Altre Informazioni Dove posso ottenere maggiori informazioni www.devleap.it www.microsoft.com/msdn/italy msdn.microsoft.com Developer resources Microsoft Visual Studio.NET Microsoft .NET Framework SDK Microsoft Developer Network
Gestione della memoria e delle risorse in .NET  I vostri feedback sono importanti Scriveteci Grazie della partecipazione A presto [email_address]

Asp.net gestione della_memoria

  • 1.
    Gestione della memoriae delle risorse in .NET Marco Russo MCSD MCAD MCSE+I MCSA MCDBA MCT Mail: marco@devleap.it Italian blog: http://blogs.devleap.com/marco.blog
  • 2.
    Iscriviti su www.microsoft.com/italy/technicalconference/default.mspxGestione e infrastruttura di rete Smart Client: la potenza del PC e la connettività Web Gestione dell'infrastruttura dei sistemi di produttività aziendale Sicurezza e interoperabilità Guest Speaker Bill Gates Non perdere l'appuntamento dedicato ai professionisti IT e agli sviluppatori
  • 3.
    DevLeap: chi siamowww.DevLeap.it Un gruppo di 5 persone con tanta voglia di Studiare a fondo le tecnologie Capire il “behind the scenes” Implementare soluzioni reali Confrontarsi con le problematiche reali Sperimentare nuove idee Facciamo Corsi, Conferenze, Training Scriviamo libri http://www.devleap.it/fullcontact
  • 4.
    Chi siete ?Chi usa già .NET? Chi usa C#? Chi usa VB.NET? Chi usa C++/Managed C++? Chi conosce Java?
  • 5.
    Agenda Perché ilGarbage Collector Algoritmo mark-and-compact F-reachable queue Pattern IDisposable Resurrection Weak Reference Algoritmo generazionale Thread e Garbage Collector Garbage Collector vs. Heap Win32
  • 6.
    Perché il GarbageCollector La gestione della memoria è fondamentale nei linguaggi a oggetti I modelli tradizionali sono deterministici: Distruzione manuale (C, C++, Win32) Reference counting (COM, VB) Minore uso di memoria Maggiore consumo di CPU Modelli alternativi (euristici): Mark/sweep Copy collect Mark/compact
  • 7.
    Algoritmi di GarbageCollection Mark/Sweep Segna tutti gli oggetti “raggiungibili” Gli oggetti non raggiungibili vengono distrutti Crea frammentazione memoria libera Copy Collect Copia tutti gli oggetti raggiungibili in una nuova zona di memoria (contigua) Aggiorna tutti i riferimenti agli oggetti copiati Libera la zona di memoria esaminata Mark/Compact Soluzione ibrida, implementata da .NET
  • 8.
    Algoritmo mark-and-compact Allocazionesequenziale di tutti gli oggetti Alte prestazioni a.b a2.b A a = new A(); a.b = new B(); A a2 = new A(); a2.b = new B(); NextObjPtr a a a2 b
  • 9.
    Algoritmo mark-and-compact L’operazionedi Garbage Collection raccoglie gli oggetti non raggiungibili Avviene automaticamente quando l’heap cresce Pilotata manualmente da GC.Collect() Due fasi: Mark individua la memoria non raggiungibile Compact sposta tutti gli oggetti all’inizio dell’heap, aggiornando tutti i riferimenti
  • 10.
    GC fase 1:Mark Identifica gli oggetti referenziati, raggiungibili da posti conosciuti ( root set ): Proprietà AppDomain Registri CPU Slot TLS Variabili locali sullo stack Membri statici delle classi Iterazione ricorsiva degli oggetti referenziati per “segnare” tutti gli oggetti raggiungibili
  • 11.
    GC fase 2:Compact Tutti gli oggetti “raggiungibili” vengono spostati all’inizio dell’heap Tutti i riferimenti vengono aggiornati Lo spazio libero non resta frammentato
  • 12.
    GC fase 1:Mark NextObjPtr Oggetti “vivi” Oggetti non raggiungibili Spazio libero Root set
  • 13.
    GC fase 2:Compact NextObjPtr Oggetti “vivi” Spazio libero Root set Spazio recuperato
  • 14.
    Finalization queue Glioggetti che possiedono un finalizer non possono essere subito raccolti dal GC In C# il distruttore è sinonimo di Finalize Chiamata del finalizer dopo collection Alla creazione di un oggetto che possiede Finalize, viene aggiunto un riferimento all’oggetto nella Finalization queue Durante il GC, gli oggetti referenziati solo da Finalization queue finiscono in F-reachable queue
  • 15.
    F-reachable queue F-reachablequeue contiene riferimento a oggetti con finalizer da richiamare Alimentata da Finalization queue I riferimenti contenuti da Finalization e F-reachable queue diventano parte del root set
  • 16.
    F-reachable queue Oggetti“vivi” In attesa di Finalize Spazio libero Root set F-reachable queue
  • 17.
    F-reachable queue F-reachablequeue viene smaltita in un thread dedicato che agisce in background GC.WaitForPendingFinalizer() per attendere che F-reachable queue sia vuota Ordine di chiamata su Finalize() non segue eventuali gerarchie padre-figlio Per queste esigenze usare Dispose() Evitare di implementare Finalize se non è necessario (migliori prestazioni)
  • 18.
    Pattern IDisposable Inalcuni casi serve un comportamento di finalizzazione deterministica: Riferimenti a oggetti non gestiti Utilizzo di risorse che devono essere rilasciate appena termina il loro utilizzo Non si possono usare i finalizzatori, che non sono richiamabili direttamente Implementare l’interfaccia IDisposable
  • 19.
    Pattern IDisposable (1)class DisposeDemo : BaseClass, IDisposable { OtherRes otherRes; private disposed = false; private void freeState { if (!disposed) { // Evita doppia esecuzione // Chiude risorse allocate (es. handle non gestiti) disposed = true; } } public void Dispose () { freeState(); otherRes.Dispose();// Dispose oggetti membro base.Dispose(); // Dispose classe base (BaseClass) GC.SuppressFinalize ( this ); } ~DisposeDemo() { freeState(); } }
  • 20.
    Pattern IDisposable (2a) class BaseResource : IDisposable { OtherRes otherRes; private disposed = false; protected virtual void Dispose( bool disposing ) { if (!this.disposed) { if (disposing) { otherRes.Dispose(); // Dispose oggetti membro } CloseHandle( ... ); // Chiude risorse non gestite this.disposed = true; } } public void Dispose () { Dispose( true ); GC.SuppressFinalize ( this ); } ~DisposeDemo() { Dispose( false ); } }
  • 21.
    Pattern IDisposable (2b) class MyResource : BaseResource { // Implementa già IDisposable private disposed = false; protected override void Dispose( bool disposing ) { if (!this.disposed) { if (disposing) { // Dispose altri oggetti gestiti } // Chiude risorse non gestite this.disposed = true; } base.Dispose(); } // La funzione Dispose() è già implementata nella classe base // Il finalizzatore che chiama Dispose(bool) virtuale è già // implementato nella classe base }
  • 22.
    Componenti .NET System.Object Non implementa nessuna interfaccia System.ComponentModel. Component Implementa IDisposable col pattern (2) Deriva da MarshalByRefObject System.Windows.Forms. Control Deriva da System.ComponentModel.Component Controlli “progettabili” graficamente Per controlli interattivi derivare UserControl System.Web.UI. Control Implementa IDisposable col pattern (1)
  • 23.
    Resurrection Chiamare GC.SuppressFinalize()per eliminare l’oggetto da F-reachable queue Finalize() non verrà più richiamata Un oggetto può ri-registrare il finalizzatore chiamando GC.ReRegisterForFinalize() Tecnica chiamata “resurrection” In Finalize() si assegna this ad un oggetto raggiungibile da root set GC.ReRegisterForFinalize() garantisce la successiva ri-esecuzione di Finalize() Da non usare se non si è più che sicuri !!
  • 24.
    Resurrection public classBaseObj { // ... ~BaseObj() { Application.ObjHolder = this; GC.ReRegisterForFinalize( this ); } // ... } class Application { static public Object ObjHandler; // ... }
  • 25.
    Weak Reference UnWeak Reference è un riferimento “debole” ad un oggetto Non viene considerato come riferimento da parte del Garbage Collector Se l’oggetto viene raccolto dal GC, il Weak Reference vale null Utile per meccanismi di cache Esistono due tipi di Weak Reference: Short Weak Reference Long Weak Reference
  • 26.
    Weak Reference ShortWeak Reference Valido fino a che l’oggetto non viene finalizzato Long Weak Reference Valido anche dopo la finalizzazione Utilizzabile per Resurrection - consente di chiamare GC.ReRegisterForFinalize() WeakReference( Object target ); WeakReference( Object target, Boolean trackResurrection );
  • 27.
    Algoritmo generazionale Ottimizzazionedell’algoritmo di GC Statisticamente gli oggetti più giovani hanno una vita più breve Il GC definisce l’età in base alla durata della presenza sull’heap Generazione: insieme di elementi che sopravvivono ad un GC Le generazioni più vecchie vengono analizzate dal GC meno sovente
  • 28.
    Algoritmo generazionale Adogni GC gli oggetti che sopravvivono vengono promossi di una generazione L’implementazione attuale definisce 3 generazioni G2 Oggetti sopravvissuti a due o più GC G1 Oggetti sopravvissuti a un GC G0 Oggetti mai sottoposti a GC
  • 29.
    Algoritmo generazionale GC.GetGeneration() restituisce la generazione di un oggetto Frequenza GC su generazioni più vecchie fino a 1/10 di quelle recenti Il GC non segue i riferimenti che puntano a generazioni più vecchie GC.Collect( n ) consente di specificare quante generazioni esaminare Gli oggetti con Finalize() vengono promossi almeno una volta
  • 30.
    Large Object HeapHeap separato per oggetti di grandi dimensioni (>20k) Il loro spostamento è costoso Di solito non sono frequenti Gli oggetti non vengono spostati Simile a un tradizionale heap C/C++
  • 31.
    Thread e GarbageCollector Versioni di GC diverse per mono e multi-processore Ogni processo ha un thread separato per chiamare i Finalize() degli oggetti Priorità più alta non real-time I metodi Finalize() vengono sempre chiamati da questo thread L’operazione di GC arresta tutti i thread gestiti (root set deve essere noto) Thread hijacking Safe points Fully interruptible code
  • 32.
    Thread hijacking Inpresenza di un metodo breve, l’indirizzo di ritorno viene deviato verso il GC, che ritorna poi al percorso originale int bar() { return 42; } int foo() { int ret = bar(); return ret; } GC
  • 33.
    Safe Points Inmetodi lunghi il jitter inserisce delle chiamate che sospendono il thread se deve essere eseguito il GC int bar() { ... ... ... ... ... ... ... ... } GC Se GC in attesa, sospende il thread fino a che il GC non viene completato GC Safe Point
  • 34.
    Fully interruptible codeIn loop stretti il thread viene sospeso ed il GC esamina una tabella generata dal jitter con gli oggetti attivi (root set) ad ogni linea ... 08 while (condition) { 09 Foo a = new Foo(); 10 Bar b = new Bar(); 11 Foo c = new Foo(); 12 13 a.DoSomething(); 14 condition = c.Good(); 15 } ... {} 14 {c} 13 {a,c} 12 {a,c} 11 {a} 10 {} 09 Oggetti Linea
  • 35.
    Concurrent Garbage CollectorConfigurabile a livello di host CLR Parametro flags di CorBindToRuntimeEx() ConcurrentGC Non rallenta interfaccia utente Prestazioni complessive più basse Costruzione del grafo di raggiungibilità in background Non-concurrent GC Agisce nello stesso thread del codice utente Migliori prestazioni complessive Indicato per applicazioni server Default in file configurazione runtime: <configuration> <runtime> < gcConcurrent enabled=&quot;false&quot;/> </runtime> </configuration>
  • 36.
    Garbage Collector vs.Heap Win32 Allocazione più veloce Penalizzazione se l’oggetto ha Finalize Anche l’allocazione è più lenta Importante chiamare GC.SuppressFinalize() su Dispose() Heap C++ per build Debug estremamente più lento (anche 1.000 volte) Paragone difficile Il finalizzatore può essere richiamato in background, mentre il distruttore è sempre sincrono nel thread chiamante
  • 37.
    Considerazioni finali Noninteragire direttamente con GC se non c’è un buon motivo Tentare di risolvere un bug chiamando GC.Collect() non è un buon motivo! Evitare finalizzatori se le prestazioni sono importanti Va più veloce della gestione Heap tradizionale Weak reference e Resurrection solo se c’è un ottimo motivo Evitare di creare più problemi di quanti se ne risolvono
  • 38.
    Riferimenti utili Demodi tutte le funzionalità di GC: C:\Program Files\Microsoft.NET\FrameworkSDK\Samples\ technologies\GarbageCollection Articoli di Jeffrey Richter su MSDN Magazine: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI.asp http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmag00/html/GCI2.asp
  • 39.
    Altre Informazioni Doveposso ottenere maggiori informazioni www.devleap.it www.microsoft.com/msdn/italy msdn.microsoft.com Developer resources Microsoft Visual Studio.NET Microsoft .NET Framework SDK Microsoft Developer Network
  • 40.
    Gestione della memoriae delle risorse in .NET I vostri feedback sono importanti Scriveteci Grazie della partecipazione A presto [email_address]

Editor's Notes

  • #32 Bisogna specificare che i thread devono essere fermati in punti noti , in modo che il GC possa sapere quali sono gli oggetti che fanno parte del root set (variabili locali sull’albero di chiamata di ogni thread)