Perdita di memoria gigante di C # / XNA

questo è il mio primo post qui. Sto facendo un gioco in Visual Studio 2010 usando XNA, e ho colpito una perdita di memoria gigante. Il mio gioco inizia con 17k ram e dopo dieci minuti arriva a 65k. Ho eseguito alcuni profiler di memoria e tutti dicono che vengono create nuove istanze dell’object String, ma non sono live. La quantità di istanze live di String non è stata modificata affatto. Sta anche creando istanze di Char [] (che mi aspetto da esso), Object [] e StringBuilder. Il mio gioco è piuttosto nuovo ma c’è troppo codice da pubblicare qui. Non ho idea di come eliminare istanze che non sono in diretta, per favore aiutatemi!

Non hai pubblicato abbastanza informazioni per fornirti più di un’ipotesi plausibile. Ecco la mia supposizione istruita:

Se stai facendo cose del genere nel tuo metodo Draw:

spriteBatch.DrawString(font, "Score: " + score, location, Color.Black); spriteBatch.DrawString(font, "Something else: " + foo, overHere, Color.Black); spriteBatch.DrawString(font, "And also: " + bar, overThere, Color.Black); 

Quindi ciascuna di queste chiamate creerà nuovi oggetti string e StringBuilder dietro la schiena ogni volta che vengono eseguiti. Poiché sono nel tuo metodo Draw , ognuno è probabilmente in esecuzione 60 volte al secondo. Questo è un sacco di oggetti temporanei assegnati!

Per verificare che sia così, utilizzare il CLR Profiler. Sembra che tu abbia già fatto questo.

Anche se questa non è una vera “fuga” – il Garbage Collector finirà per ripulirli – questo schema di allocazione è indesiderabile in un gioco. Vedi questo post sul blog su due metodi per affrontare la garbage collection in un gioco. Il metodo 1 è in genere più semplice e offre risultati migliori, quindi ne sto discutendo qui.

Vale la pena ricordare a questo punto che il GC sul PC è abbastanza veloce che le allocazioni come questa non contano davvero . Il GC pulirà piccoli oggetti (come le tue corde temporanee) con un piccolo overhead.

Su Xbox 360, d’altra parte, anche la produzione di piccole quantità di spazzatura come questa regolarmente può causare seri problemi di prestazioni. (Non sono sicuro del WP7, ma lo tratterei personalmente come la Xbox – con attenzione!)

come lo aggiustiamo?

La risposta è semplice: DrawString accetterà un’istanza di StringBuilder al posto della string . Creare un’istanza di StringBuilder e quindi riutilizzarla ogni volta che è necessario creare una stringa personalizzata.

Si noti che la conversione di un numero o di un altro object in una stringa, implicitamente o mediante il suo metodo ToString() , causerà anche allocazioni. Quindi potresti dover scrivere il tuo codice personalizzato da aggiungere a StringBuilder senza causare allocazioni.

Ecco uno che uso, sotto forma di un metodo di estensione, per accodare numeri interi a una stringa senza allocare:

 public static class StringBuilderExtensions { // 11 characters will fit -4294967296 static char[] numberBuffer = new char[11]; /// Append an integer without generating any garbage. public static StringBuilder AppendNumber(this StringBuilder sb, Int32 number) { bool negative = (number < 0); if(negative) number = -number; int i = numberBuffer.Length; do { numberBuffer[--i] = (char)('0' + (number % 10)); number /= 10; } while(number > 0); if(negative) numberBuffer[--i] = '-'; sb.Append(numberBuffer, i, numberBuffer.Length - i); return sb; } } 

Non ci sono perdite di memoria in C # (o, beh, sono molto difficili da ottenere). Quello che stai vivendo è normale. Il garbage collector non “sente” come se avesse bisogno di raccogliere memoria, quindi non lo fa. Ogni volta che la memoria si esaurisce, si verificherà la garbage collection. Se sei assolutamente certo che non stai mantenendo riferimenti non necessari alle tue string , allora va tutto bene.

Se si desidera forzare un ciclo di GC, utilizzare GC.Collect() .