web-dev-qa-db-fra.com

Une fuite de mémoire est-elle créée si un MemoryStream dans .NET n'est pas fermé?

J'ai le code suivant:

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

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

Y a-t-il une chance que le MemoryStream que j'ai alloué échoue d'une manière ou d'une autre à être éliminé plus tard?

J'ai un examen par les pairs insistant pour que je ferme manuellement cela, et je ne trouve pas les informations pour dire s'il a un point valide ou non.

106
Coderer

Si quelque chose est jetable, vous devez toujours le jeter. Vous devez utiliser une instruction using dans votre méthode bar() pour vous assurer que ms2 est éliminé.

Il finira par être nettoyé par le garbage collector, mais il est toujours recommandé d'appeler Dispose. Si vous exécutez FxCop sur votre code, il le marquerait comme un avertissement.

58
Rob Prouse

Vous n'aurez rien à fuir - du moins dans l'implémentation actuelle.

Appeler Dispose ne nettoiera pas la mémoire utilisée par MemoryStream plus rapidement. Cela va empêcher votre flux d'être viable pour les appels en lecture/écriture après l'appel, qui peuvent ou non vous être utiles.

Si vous êtes absolument sûr que vous jamais voulez passer d'un MemoryStream à un autre type de flux, cela ne vous fera aucun mal de ne pas appeler Dispose. Cependant, c'est généralement une bonne pratique, en partie parce que si vous changez do pour utiliser un Stream différent, vous ne voulez pas être mordu par un bug difficile à trouver parce que vous avez choisi la solution de facilité au début sur. (D'un autre côté, il y a l'argument YAGNI ...)

L'autre raison de le faire de toute façon est qu'une nouvelle implémentation peut introduire des ressources qui seraient libérées sur Dispose.

159
Jon Skeet

Oui, il y a une fuite , selon la façon dont vous définissez LEAK et combien PLUS TARD vous voulez dire ...

Si par fuite, vous voulez dire "la mémoire reste allouée, indisponible pour une utilisation, même si vous avez fini de l'utiliser" et par ce dernier, vous voulez dire à tout moment après avoir appelé dispose, alors oui, il peut y avoir une fuite, bien qu'elle ne soit pas permanente (c'est-à-dire pour la durée de vie de vos applications).

Pour libérer la mémoire gérée utilisée par le MemoryStream, vous devez annuler la référence, en annulant votre référence à celui-ci, afin qu'il devienne immédiatement éligible pour le ramasse-miettes. Si vous ne le faites pas, vous créez une fuite temporaire à partir du moment où vous avez fini de l'utiliser, jusqu'à ce que votre référence soit hors de portée, car en attendant, la mémoire ne sera pas disponible pour l'allocation.

L'avantage de l'instruction using (au lieu d'appeler simplement dispose) est que vous pouvez DÉCLARER votre référence dans l'instruction using. Lorsque l'instruction using se termine, non seulement est appelé, mais votre référence sort du domaine d'application, annulant effectivement la référence et rendant votre objet éligible pour le garbage collection immédiatement sans vous obliger à vous rappeler d'écrire le code "reference = null".

Bien que ne pas déférer immédiatement quelque chose ne soit pas une fuite de mémoire "permanente" classique, cela a certainement le même effet. Par exemple, si vous conservez votre référence au MemoryStream (même après avoir appelé dispose), et un peu plus loin dans votre méthode, vous essayez d'allouer plus de mémoire ... la mémoire utilisée par votre flux de mémoire encore référencé ne sera pas disponible jusqu'à ce que vous annuliez la référence ou qu'elle devienne hors de portée, même si vous avez appelé disposer et que vous avez fini de l'utiliser.

25
Triynko

C'est déjà répondu, mais j'ajouterai simplement que le bon vieux principe de la dissimulation d'informations signifie que vous voudrez peut-être à un moment donné refactoriser:

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

à:

Stream foo()
{    
   ...
}

Cela souligne que les appelants ne devraient pas se soucier du type de flux renvoyé et permet de modifier l'implémentation interne (par exemple lors de moqueries pour les tests unitaires).

Vous devrez alors avoir des ennuis si vous n'avez pas utilisé Dispose dans votre implémentation de barre:

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

Tous les flux implémentent IDisposable. Enveloppez votre flux de mémoire dans une instruction using et vous serez bien et dandy. Le bloc d'utilisation garantira que votre flux est fermé et éliminé quoi qu'il arrive.

partout où vous appelez Foo, vous pouvez le faire en utilisant (MemoryStream ms = foo ()) et je pense que vous devriez toujours être d'accord.

5
Nick

L'appel de .Dispose() (ou le wrapping avec Using) n'est pas requis.

La raison pour laquelle vous appelez .Dispose() est libérez la ressource dès que possible.

Pensez, par exemple, au serveur Stack Overflow, où nous avons un ensemble limité de mémoire et des milliers de demandes qui arrivent. Nous ne voulons pas attendre la récupération de place planifiée, nous voulons libérer cette mémoire dès que possible afin qu'elle soit disponible pour les nouvelles demandes entrantes.

5
Jeff Atwood

Je recommanderais d'encapsuler le MemoryStream dans bar() dans une instruction using principalement pour la cohérence:

  • À l'heure actuelle, MemoryStream ne libère pas de mémoire sur .Dispose(), mais il est possible qu'à un moment donné dans le futur, ou que vous (ou quelqu'un d'autre dans votre entreprise) la remplaciez par votre propre MemoryStream personnalisé qui ne le fait pas. , etc.
  • Cela aide à établir un modèle dans votre projet pour garantir tous les flux sont éliminés - la ligne est plus fermement tracée en disant "tous les flux doivent être éliminés" au lieu de "certains flux doivent être éliminés, mais certains ceux-là n'ont pas à "...
  • Si jamais vous changez le code pour autoriser le retour d'autres types de Streams, vous devrez quand même le changer pour le supprimer.

Une autre chose que je fais habituellement dans des cas comme foo() lors de la création et du retour d'un IDisposable est de s'assurer que toute défaillance entre la construction de l'objet et le return est interceptée par une exception, supprime l'objet et renverse l'exception:

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;
}
2
Chris R. Donnelly

Vous ne perdrez pas de mémoire, mais votre réviseur de code est correct pour indiquer que vous devez fermer votre flux. C'est poli de le faire.

La seule situation dans laquelle vous pourriez perdre de la mémoire est lorsque vous laissez accidentellement une référence au flux et ne le fermez jamais. Vous ne perdez pas vraiment de mémoire, mais vous êtes allongeant inutilement la durée pendant laquelle vous prétendez l'utiliser.

2
OwenP

Si un objet implémente IDisposable, vous devez appeler la méthode .Dispose lorsque vous avez terminé.

Dans certains objets, Dispose signifie la même chose que Fermer et vice versa, dans ce cas, l'un ou l'autre est bon.

Maintenant, pour votre question particulière, non, vous ne fuira pas de mémoire.