Question

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;
}

Existe-t-il une chance que le MemoryStream que j'ai alloué ne soit pas éliminé ultérieurement?

J'ai une évaluation par les pairs qui insiste pour que je ferme manuellement cette information et je ne trouve pas les informations permettant de savoir s'il a un argument valable ou non.

Était-ce utile?

La solution

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

Il sera éventuellement nettoyé par le ramasse-miettes, mais c’est toujours une bonne pratique d’éliminer. Si vous exécutez FxCop sur votre code, il sera signalé comme un avertissement.

Autres conseils

Vous ne ferez rien, du moins dans la mise en œuvre actuelle.

L’appel de Dispose ne nettoiera pas plus rapidement la mémoire utilisée par MemoryStream. Cela empêchera votre flux d'être viable pour les appels en lecture / écriture après l'appel, ce qui peut vous être utile ou non.

Si vous êtes absolument certain de ne jamais vouloir passer d'un MemoryStream à un autre type de flux, vous n'aurez aucun mal à ne pas appeler Dispose. Toutefois, c’est généralement une bonne pratique, en partie parce que si vous changez pour utiliser un autre flux, vous ne voulez pas vous faire piéger par un bogue difficile à trouver car vous avez choisi la solution de facilité le plus tôt possible. sur. (D'autre part, il y a l'argument YAGNI ...)

L’autre raison de le faire malgré tout est qu’une nouvelle implémentation peut introduire des ressources qui seraient libérées sur Dispose.

Oui, il y a une fuite , selon votre définition de FUITE et votre degré de signification PLUS TARD ...

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

Pour libérer la mémoire gérée utilisée par MemoryStream, vous devez ne pas la référencer , en annulant votre référence, elle devient dès lors éligible à la récupération de place. Si vous ne le faites pas, vous créez une fuite temporaire à partir du moment où vous l’utilisez, jusqu’à ce que votre référence devienne hors de portée, car entre-temps, la mémoire ne sera pas disponible pour l’allocation.

L'avantage de l'instruction using (par rapport à simplement appeler dispose) est que vous pouvez déclarer votre référence dans l'instruction using. Lorsque l'instruction using se termine, non seulement la commande dispose est appelée, mais votre référence sort de la portée, ce qui la supprime et rend votre objet immédiatement éligible pour la récupération de place sans qu'il ne soit nécessaire de vous rappeler d'écrire le paramètre " reference = null " code.

Bien que ne pas faire référence immédiatement à quelque chose ne soit pas un classique "permanent" fuite de mémoire, il a certainement le même effet. Par exemple, si vous conservez votre référence au MemoryStream (même après l'appel de disposer) et si vous essayez 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 toujours référencé ne sera pas disponible. jusqu'à ce que vous annuliez la référence ou qu'elle disparaisse de sa portée, même si vous avez appelé la suppression et que vous avez fini de l'utiliser.

Cela a déjà été répondu, mais j'ajouterai simplement que le bon vieux principe de la dissimulation d'informations signifie que vous voudrez peut-être ultérieurement refactoriser:

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

à:

Stream foo()
{    
   ...
}

Cela souligne que les appelants ne doivent pas se soucier du type de flux renvoyé et permet de modifier l’implémentation interne (par exemple, lorsqu’on se moque du test unitaire).

Vous aurez alors des problèmes si vous n'avez pas utilisé Dispose dans votre implémentation de barres:

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

Tous les flux implémentent IDisposable. Enveloppez votre flux de mémoire dans une instruction using et tout ira bien pour vous. Le bloc using garantit que votre flux est fermé et éliminé, quoi qu’il en soit.

Où que vous appeliez Foo, vous pouvez utiliser (MemoryStream ms = foo ()) et je pense que vous devriez toujours être ok.

L'appel de .Dispose () (ou le retour à la ligne avec à l'aide de ) n'est pas requis.

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

Pensez, par exemple, au serveur Stack Overflow, où nous avons un ensemble de mémoire limité et des milliers de demandes entrantes. Nous ne voulons pas attendre la collecte de mémoire programmée, nous voulons libérer cette mémoire dès que possible. il est donc disponible pour les nouvelles demandes entrantes.

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

La seule situation dans laquelle vous risquez de perdre de la mémoire est lorsque vous laissez accidentellement une référence au flux et ne le fermez jamais. Vous ne perdez toujours pas vraiment la mémoire, mais vous augmentez inutilement la durée d'utilisation de votre mémoire.

Je recommanderais d'encapsuler le MemoryStream dans bar () dans une instruction à l'aide de principalement par souci de cohérence:

  • À l'heure actuelle, MemoryStream ne libère pas de mémoire sur .Dispose () , mais il est possible que cela se produise à l'avenir, ou que vous (ou quelqu'un de votre société) le remplacez. avec votre propre MemoryStream personnalisé qui le fait, etc.
  • Il est utile d’établir un modèle dans votre projet pour vous assurer que tous tous les flux soient éliminés - la ligne est mieux tracée en indiquant que "tous les flux doivent être éliminés". au lieu de "certains flux doivent être éliminés, mais certains ne sont pas obligés" ...
  • Si vous modifiez un jour le code pour autoriser le renvoi d'autres types de flux, vous devez le modifier de toute façon.

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 tout échec entre la construction de l'objet et le return est intercepté. une exception, supprime l'objet et renvoie 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;
}

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 Close et vice versa. Dans ce cas, cela est correct.

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

Je ne suis pas un expert .net, mais le problème ici est peut-être celui des ressources, à savoir le descripteur de fichier, et non de la mémoire. Je suppose que le ramasse-miettes va éventuellement libérer le flux et fermer le handle, mais je pense qu'il serait toujours préférable de le fermer explicitement, pour être sûr de vider le contenu sur le disque.

L'élimination des ressources non gérées n'est pas déterministe dans les langages récupérés. Même si vous appelez explicitement Dispose, vous n'avez absolument aucun contrôle sur le moment où la mémoire de sauvegarde est réellement libérée. Dispose est appelé implicitement lorsqu'un objet sort de sa portée, que ce soit en quittant une instruction using ou en affichant la pile d'appels depuis une méthode subordonnée. Ceci étant dit, parfois, l'objet peut en réalité être un wrapper pour une ressource gérée (un fichier, par exemple). C'est pourquoi il est recommandé de fermer explicitement les instructions finally ou d'utiliser l'instruction using. A la vôtre

MemorySteram n'est rien d'autre qu'un tableau d'octets, qui est un objet géré. Oubliez de jeter ou de fermer cela n'a pas d'effet secondaire autre que la finalisation.
Il suffit de cocher la méthode Constuctor ou Flush de MemoryStream dans le réflecteur et vous comprendrez pourquoi vous n'avez pas à vous soucier de le fermer ou de le jeter autrement que pour suivre simplement les bonnes pratiques.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top