Domanda

Quali sono tutti i modi possibili in cui possiamo ottenere perdite di memoria in .NET?

Ne conosco due:

  1. Non annullare correttamente la registrazione Gestori/delegati di eventi.
  2. Non eliminare i controlli figlio dinamici in Windows Forms:

Esempio:

// Causes Leaks  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// Correct Code  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

Aggiornamento:L'idea è quella di elencare le trappole comuni che non sono troppo ovvie (come quelle sopra).Di solito si pensa che le perdite di memoria non siano un grosso problema a causa del garbage collector.Non come una volta in C++.


Bella discussione ragazzi, ma lasciatemi chiarire...per definizione, se non è rimasto alcun riferimento a un oggetto in .NET, prima o poi verrà sottoposto a Garbage Collected.Quindi questo non è un modo per indurre perdite di memoria.

Nell'ambiente gestito, lo considererei una perdita di memoria se avessi un riferimento involontario a qualsiasi oggetto di cui non sei a conoscenza (da qui i due esempi nella mia domanda).

Quindi, quali sono i vari modi possibili in cui può verificarsi una tale perdita di memoria?

È stato utile?

Soluzione

Blocca il thread del finalizzatore.Nessun altro oggetto verrà sottoposto a Garbage Collection finché il thread del finalizzatore non verrà sbloccato.Pertanto la quantità di memoria utilizzata aumenterà sempre di più.

Ulteriori letture: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

Altri suggerimenti

Ciò non causa realmente perdite, rende solo più lavoro per il GC:

// slows GC
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// better  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

// best
using( Label label = new Label() )
{ 
    this.Controls.Add(label);  
    this.Controls.Remove(label);  
}

Lasciare i componenti usa e getta in giro in questo modo non è mai un grosso problema in un ambiente gestito come .Net: questa è una parte importante di ciò che significa "gestito".

Sicuramente rallenterai la tua app.Ma non lascerai un pasticcio per nient'altro.

Impostazione del GridControl.DataSource proprietà direttamente senza utilizzare un'istanza della classe BindingSource (http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx).

Ciò ha causato perdite nella mia applicazione che mi ci è voluto un po' di tempo per rintracciare con un profiler, alla fine ho trovato questa segnalazione di bug a cui Microsoft ha risposto: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

È divertente che nella documentazione della classe BindingSource Microsoft cerchi di farla passare per una classe legittima e ben congegnata, ma penso che l'abbiano appena creata per risolvere una perdita fondamentale riguardante i gestori di valuta e l'associazione dei dati ai controlli della griglia.

Fai attenzione a questo, scommetto che ci sono assolutamente un sacco di applicazioni che perdono a causa di questo!

Non è possibile fornire un elenco completo...è molto simile a chiedere "Come puoi bagnarti?"

Detto questo, assicurati di chiamare Dispose() su tutto ciò che implementa IDisposable e assicurati di implementare IDisposable su tutti i tipi che consumano risorse non gestite di qualsiasi tipo.

Di tanto in tanto, esegui qualcosa come FxCop sulla tua codebase per aiutarti a far rispettare quella regola: rimarrai sorpreso dalla profondità con cui alcuni oggetti usa e getta vengono sepolti all'interno di un framework applicativo.

Eccezioni nei metodi Finalize (o chiamate Dispose da un Finaliser) che impediscono la corretta eliminazione delle risorse non gestite.Uno comune è dovuto al programmatore assumendo quale ordine gli oggetti verranno eliminati e il tentativo di rilasciare oggetti peer che sono già stati eliminati, con conseguente eccezione e la mancata chiamata del resto del metodo Finalise/Dispose from Finalize.

Ho altri 4 elementi da aggiungere a questa discussione:

  1. La terminazione dei thread (Thread.Abort()) che hanno creato controlli dell'interfaccia utente senza una preparazione adeguata per un evento di questo tipo può comportare un utilizzo della memoria in attesa.

  2. L'accesso alle risorse non gestite tramite Pinvoke e la mancata pulizia delle stesse può causare perdite di memoria.

  3. Modifica di oggetti stringa di grandi dimensioni.Non necessariamente una perdita di memoria, una volta fuori dall'ambito, GC se ne occuperà, tuttavia, dal punto di vista delle prestazioni, il tuo sistema potrebbe subire un duro colpo se stringhe di grandi dimensioni vengono modificate spesso perché non puoi realmente dipendere da GC per garantire che l'impronta del tuo programma sia minimo.

  4. Creazione frequente di oggetti GDI per eseguire disegni personalizzati.Se si esegue spesso il lavoro GDI, riutilizzare un singolo oggetto GDI.

Chiamare IDisposable ogni volta è il punto di partenza più semplice e sicuramente un modo efficace per catturare tutti i frutti di perdite di memoria a basso rischio nella base di codice.Tuttavia, non è sempre sufficiente.Ad esempio, è anche importante comprendere come e quando il codice gestito viene generato in fase di esecuzione e che, una volta caricati nel dominio dell'applicazione, gli assembly non vengono mai scaricati, il che può aumentare l'ingombro dell'applicazione.

Per evitare perdite di memoria .NET:

1) Utilizzare il costrutto 'using' (o il costrutto 'try-finally') ogni volta che viene creato un oggetto con l'interfaccia 'IDisposable'.

2) Rendi le classi "IDisposable" se creano un thread o aggiungono un oggetto a una raccolta statica o di lunga durata.Ricorda che un "evento" C# è una raccolta.

Ecco un breve articolo su Suggerimenti per prevenire perdite di memoria.

Stai parlando di utilizzo imprevisto della memoria o di perdite effettive?I due casi che hai elencato non sono esattamente fughe di notizie;sono casi in cui gli oggetti rimangono più a lungo del previsto.

In altre parole, sono riferimenti che la persona che li chiama perdite di memoria non conosceva o aveva dimenticato.

Modificare:Oppure sono veri e propri bug nel Garbage Collector o nel codice non gestito.

Modifica 2:Un altro modo di pensare a questo è assicurarsi sempre che i riferimenti esterni ai tuoi oggetti vengano rilasciati in modo appropriato.Esterno significa codice fuori dal tuo controllo.Ogni caso in cui ciò accade è un caso in cui è possibile "perdere" memoria.

  1. Mantenere in giro riferimenti ad oggetti che non ti servono più.

Riguardo altri commenti: un modo per garantire che Dispose venga chiamato è utilizzare using...quando la struttura del codice lo consente.

Una cosa davvero inaspettata per me è questa:

Region oldClip = graphics.Clip;
using (Region newClip = new Region(...))
{
    graphics.Clip = newClip;
    // draw something
    graphics.Clip = oldClip;
}

Dov'è la perdita di memoria?Esatto, avresti dovuto smaltirlo oldClip, pure!Perché Graphics.Clip è una delle rare proprietà che restituisce un nuovo oggetto usa e getta ogni volta che viene invocato il getter.

Tess Fernandez Ha ottimi post sul blog sulla ricerca e il debug delle perdite di memoria.Laboratorio 6 Laboratorio 7

Molte delle cose che possono causare perdite di memoria nei linguaggi non gestiti possono comunque causare perdite di memoria nei linguaggi gestiti.Per esempio, cattive politiche di memorizzazione nella cache può causare perdite di memoria.

Ma come hanno detto Greg e Danny, non esiste un elenco completo.Tutto ciò che può comportare il mantenimento della memoria dopo la sua vita utile può causare una perdita.

I thread in stallo non rilasceranno mai le radici.Ovviamente si potrebbe sostenere che lo stallo rappresenta un problema più grande.

Un thread del finalizzatore bloccato impedirà l'esecuzione di tutti i finalizzatori rimanenti e quindi impedirà il recupero di tutti gli oggetti finalizzabili (poiché sono ancora rootati dall'elenco freachable).

Su una macchina multi CPU è possibile creare oggetti finalizzabili più velocemente di quanto il thread del finalizzatore possa eseguire i finalizzatori.Finché ciò viene sostenuto, "perderai" la memoria.Probabilmente non è molto probabile che ciò accada in natura, ma è facile riprodursi.

L'heap di oggetti di grandi dimensioni non è compattato, pertanto è possibile che si verifichi una perdita di memoria a causa della frammentazione.

Esistono numerosi oggetti che devono essere liberati manualmente.Per esempio.oggetti remoti senza lease e assembly (è necessario scaricare AppDomain).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top