Si verifica una perdita di memoria se un MemoryStream in .NET non viene chiuso?

Ho il codice seguente:

MemoryStream foo(){ MemoryStream ms = new MemoryStream(); // write stuff to ms return ms; } void bar(){ MemoryStream ms2 = foo(); // do stuff with ms2 return; } 

C’è qualche possibilità che il MemoryStream che ho allocato in qualche modo non riesca a essere smaltito più tardi?

Ho una recensione tra pari che insiste che chiudo manualmente questo e non riesco a trovare le informazioni per sapere se ha un punto valido o meno.

Se qualcosa è Monouso, devi sempre Smaltirlo. Dovresti utilizzare un’istruzione using nel tuo metodo bar () per assicurarti che ms2 venga eliminato.

Alla fine verrà ripulito dal garbage collector, ma è sempre buona pratica smaltire. Se esegui FxCop sul tuo codice, lo contrassegna come un avvertimento.

Non perderai nulla, almeno nell’attuale implementazione.

Chiamare Dispose non ripulirà più velocemente la memoria utilizzata da MemoryStream. Ciò impedirà al tuo stream di essere valido per le chiamate di lettura / scrittura dopo la chiamata, che potrebbero essere o non essere utili a te.

Se sei assolutamente sicuro di non voler passare da un MemoryStream ad un altro tipo di stream, non ti farà alcun danno per non chiamare Dispose. Tuttavia, in genere è una buona pratica in parte perché se cambi mai per utilizzare un stream diverso, non vuoi essere morso da un bug difficile da trovare perché hai scelto la via d’uscita facile all’inizio. (D’altra parte, c’è l’argomento YAGNI …)

L’altro motivo per farlo in ogni caso è che una nuova implementazione può introdurre risorse che potrebbero essere liberate in Dispose.

Sì, c’è una perdita , a seconda di come definisci LEAK e quanto DURANTE vuoi dire …

Se per perdita intendi “la memoria rimane allocata, non disponibile per l’uso, anche se hai finito di usarla” e da quest’ultima intendi in qualsiasi momento dopo aver chiamato la disposizione, quindi sì, ci può essere una perdita, anche se non è permanente (cioè per la durata del runtime delle tue applicazioni).

Per liberare la memoria gestita utilizzata da MemoryStream, è necessario deselezionarlo , annullando il riferimento ad esso, in modo che diventi immediatamente idoneo per la garbage collection. Se non si riesce a fare ciò, si crea una perdita temporanea dal momento in cui si è terminato di utilizzarla, fino a quando il riferimento non viene spostato, poiché nel frattempo la memoria non sarà disponibile per l’allocazione.

Il beneficio dell’istruzione using (oltre la semplice chiamata di dispose) è che puoi DECLARE il tuo riferimento nell’istruzione using. Quando l’istruzione using termina, non solo viene chiamata la chiamata, ma la tua referenza va fuori dal campo di applicazione, annullando effettivamente il riferimento e rendendo il tuo object idoneo per la garbage collection immediatamente senza richiedere di ricordare di scrivere il codice “reference = null”.

Pur non riuscendo a svelare qualcosa subito non è una classica perdita di memoria “permanente”, ma ha sicuramente lo stesso effetto. Ad esempio, se si mantiene il riferimento a MemoryStream (anche dopo aver chiamato dispose), e un po ‘più in basso nel metodo si tenta di allocare più memoria … la memoria in uso dal stream di memoria ancora referenziato non sarà disponibile a te fino a quando non annulli il riferimento o se ne esce dall’ambito di applicazione, anche se hai chiamato smaltire e hai finito di usarlo.

Questo ha già una risposta, ma aggiungerò che il buon vecchio principio di hide le informazioni significa che in futuro vorrete refactoring:

 MemoryStream foo() { MemoryStream ms = new MemoryStream(); // write stuff to ms return ms; } 

a:

 Stream foo() { ... } 

Ciò sottolinea che ai chiamanti non dovrebbe importare quale tipo di stream viene restituito e consente di modificare l’implementazione interna (ad esempio quando si deride il test dell’unità).

Dovrai quindi essere nei guai se non hai usato Dispose nella tua implementazione della barra:

 void bar() { using (Stream s = foo()) { // do stuff with s return; } } 

Tutti i flussi implementano IDisposable. Avvolgi il tuo stream di memoria in una dichiarazione usando e starai bene e dandy. Il blocco using assicurerà che il tuo stream sia chiuso e smaltito indipendentemente da cosa.

ovunque tu chiami Foo puoi farlo usando (MemoryStream ms = foo ()) e penso che dovresti essere ancora ok.

Chiamare .Dispose() (o il wrapping con Using ) non è richiesto.

La ragione per cui chiami .Dispose() è di rilasciare la risorsa il prima ansible .

Pensa in termini di, ad esempio, il server Stack Overflow, in cui abbiamo un limitato set di memoria e migliaia di richieste in arrivo. Non vogliamo aspettare la raccolta dei rifiuti programmata, vogliamo rilasciare la memoria ASAP in modo che sia disponibile per nuove richieste in arrivo.

Non perderai memoria, ma il tuo revisore del codice è corretto per indicare che devi chiudere il tuo stream. È educato farlo.

L’unica situazione in cui si potrebbe perdere memoria è quando si lascia accidentalmente un riferimento allo stream e non si chiude mai. Non stai ancora perdendo memoria, ma stai inutilmente estendendo la quantità di tempo che pretendi di usarlo.

Consiglierei di avvolgere il MemoryStream in bar() in un’istruzione using principalmente per consistenza:

  • In questo momento MemoryStream non libera memoria su .Dispose() , ma è ansible che in un certo momento in futuro potrebbe, o tu (o qualcun altro della tua azienda) potrebbe sostituirlo con il tuo MemoryMustream personalizzato che lo fa, ecc.
  • Aiuta a stabilire un modello nel tuo progetto per assicurare che tutti gli Stream vengano eliminati – la linea è disegnata più saldamente dicendo “tutti gli Stream devono essere eliminati” invece di “alcuni Stream devono essere eliminati, ma alcuni non devono” …
  • Se cambi mai il codice per consentire il ritorno di altri tipi di stream, dovrai comunque modificarlo per eliminarlo.

Un’altra cosa che di solito faccio in casi come foo() durante la creazione e la restituzione di un IDisposable è quella di assicurare che qualsiasi errore tra la costruzione dell’object e il return sia catturato da un’eccezione, elimina l’object e fa retrocedere l’eccezione:

 MemoryStream x = new MemoryStream(); try { // ... other code goes here ... return x; } catch { // "other code" failed, dispose the stream before throwing out the Exception x.Dispose(); throw; } 

Se un object implementa IDisposable, è necessario chiamare il metodo .Dispose quando hai finito.

In alcuni oggetti, Dispose significa lo stesso di Close e viceversa, in tal caso, o è buono.

Ora, per la tua particolare domanda, no, non perderai memoria.

Non sono un esperto di .net, ma forse il problema qui sono le risorse, vale a dire l’handle del file e non la memoria. Immagino che il garbage collector alla fine libererà il stream e chiuderà il manico, ma penso che sarebbe sempre meglio chiuderlo esplicitamente, per essere sicuri di svuotare il contenuto sul disco.

Lo smaltimento di risorse non gestite non è deterministico nelle lingue raccolte con garbage. Anche se chiami Dispose in modo esplicito, non hai assolutamente alcun controllo su quando la memoria di supporto viene effettivamente liberata. Dispose viene chiamato implicitamente quando un object esce dall’ambito, sia uscendo da un’istruzione using, sia facendo scattare il callstack da un metodo subordinato. Detto questo, a volte l’object potrebbe essere effettivamente un wrapper per una risorsa gestita (ad es. File). Questo è il motivo per cui è buona norma chiudere esplicitamente le affermazioni finali o utilizzare l’istruzione using. Saluti

MemorySteram non è altro che una matrice di byte, che è object gestito. Dimenticare di disporre o chiudere questo non ha alcun effetto collaterale oltre a oltre la fine della finalizzazione.
Basta controllare il metodo di constuctor o flush di MemoryStream nel reflector e sarà chiaro il motivo per cui non è necessario preoccuparsi di chiuderlo o smaltirlo se non per il semplice fatto di seguire le buone pratiche.