c# - example - ¿Se crea una pérdida de memoria si MemoryStream en.NET no está cerrado?
memorystream example c# (12)
Esto ya está respondido, pero solo agregaré que el principio bien pasado de ocultación de la información significa que en algún momento futuro puede refactorizarse:
MemoryStream foo()
{
MemoryStream ms = new MemoryStream();
// write stuff to ms
return ms;
}
a:
Stream foo()
{
...
}
Esto enfatiza que a las personas que llaman no les debería importar qué tipo de Stream se está devolviendo, y hace posible cambiar la implementación interna (por ejemplo, cuando se burla de las pruebas unitarias).
A continuación, deberá tener problemas si no ha utilizado Eliminar en la implementación de su barra:
void bar()
{
using (Stream s = foo())
{
// do stuff with s
return;
}
}
Tengo el siguiente código:
MemoryStream foo(){
MemoryStream ms = new MemoryStream();
// write stuff to ms
return ms;
}
void bar(){
MemoryStream ms2 = foo();
// do stuff with ms2
return;
}
¿Hay alguna posibilidad de que el MemoryStream que he asignado de alguna manera no se elimine más tarde?
Tengo una revisión por pares que insiste en que cierre manualmente esto, y no puedo encontrar la información para decir si tiene un punto válido o no.
La eliminación de recursos no gestionados no es determinista en los lenguajes recolectados. Incluso si llama a Dispose explícitamente, no tiene absolutamente ningún control sobre cuándo realmente se libera la memoria de respaldo. Dispose se llama implícitamente cuando un objeto sale del ámbito, ya sea saliendo de una instrucción using o apareciendo la pila de llamadas desde un método subordinado. Dicho todo esto, a veces el objeto puede ser un contenedor para un recurso administrado (por ejemplo, un archivo). Esta es la razón por la cual es una buena práctica cerrar de manera explícita en las sentencias finally o usar la instrucción using. Aclamaciones
MemorySteram no es más que una matriz de bytes, que es un objeto gestionado. Olvídese de eliminar o cerrar esto no tiene ningún efecto secundario que no sea por encima de la finalización.
Simplemente verifique el método de relleno o relleno de MemoryStream en el reflector y quedará claro por qué no tiene que preocuparse por cerrarlo o desecharlo, solo por cuestiones de buenas prácticas.
No perderá memoria, pero su revisor de código es correcto para indicar que debe cerrar la transmisión. Es educado hacerlo.
La única situación en la que podría perder memoria es cuando accidentalmente deja una referencia a la transmisión y nunca la cierra. Todavía no está realmente perdiendo memoria, pero está extendiendo innecesariamente la cantidad de tiempo que dice que lo está usando.
No perderá nada, al menos en la implementación actual.
Calling Dispose no limpiará la memoria utilizada por MemoryStream más rápido. Esto evitará que su transmisión sea viable para las llamadas de Lectura / Escritura después de la llamada, lo que puede o no ser útil para usted.
Si está absolutamente seguro de que nunca desea pasar de un MemoryStream a otro tipo de transmisión, no le hará ningún daño no llamar a Dispose. Sin embargo, en general es una buena práctica, en parte porque si alguna vez cambias para usar un Stream diferente, no querrás ser mordido por un error difícil de encontrar porque eliges la salida fácil desde el principio. (Por otro lado, está el argumento de YAGNI ...)
La otra razón para hacerlo de todos modos es que una nueva implementación puede introducir recursos que se liberarían en Dispose.
No se .Dispose()
llamar .Dispose()
(o wrapping with Using
).
La razón por la que llama .Dispose()
es liberar el recurso lo antes posible .
Piense en términos de, digamos, el servidor , donde tenemos un conjunto limitado de memoria y miles de solicitudes entrando. No queremos esperar para la recolección programada de basura, queremos liberar esa memoria lo antes posible para que esté disponible para nuevas solicitudes entrantes.
No soy experto en .net, pero tal vez el problema aquí sean los recursos, es decir, el identificador del archivo y no la memoria. Supongo que el recolector de basura eventualmente liberará la secuencia y cerrará el identificador, pero creo que siempre será una buena práctica cerrarla explícitamente, para asegurarse de que elimine el contenido del disco.
Si algo es Desechable, siempre debes desecharlo. Debería utilizar una instrucción using en su método bar () para asegurarse de que ms2 se disipe.
Eventualmente será limpiado por el recolector de basura, pero siempre es una buena práctica desechar. Si ejecuta FxCop en su código, lo marcará como una advertencia.
Si un objeto implementa IDisposable, debe llamar al método .Dispose cuando haya terminado.
En algunos objetos, Eliminar significa lo mismo que Cerrar y viceversa, en ese caso, cualquiera de los dos es bueno.
Ahora, para su pregunta particular, no, no perderá memoria.
Todas las transmisiones implementan IDisposable. Envuelva su secuencia de memoria en una declaración de uso y estará bien y elegante. El bloque que usa se asegurará de que tu transmisión se cierre y elimine sin importar nada.
donde sea que llame a Foo puede hacerlo usando (MemoryStream ms = foo ()) y creo que todavía debería estar bien.
Yo recomendaría envolver el MemoryStream en bar()
en una instrucción using
principalmente por consistencia:
- En este momento MemoryStream no libera memoria en
.Dispose()
, pero es posible que en algún momento en el futuro lo haga, o usted (u otra persona en su compañía) podría reemplazarlo con su propio MemoryStream personalizado, etc. - Ayuda a establecer un patrón en su proyecto para asegurar que todas las transmisiones se eliminen: la línea se dibuja más firmemente diciendo que "todas las transmisiones deben desecharse" en lugar de "algunas de ellas deben eliminarse, pero otras no tienen que ser eliminadas". ...
- Si alguna vez cambia el código para permitir la devolución de otros tipos de transmisiones, deberá cambiarlo para eliminarlo de todos modos.
Otra cosa que suelo hacer en casos como foo()
al crear y devolver un IDisposable es asegurar que cualquier falla entre la construcción del objeto y la return
sea atrapada por una excepción, elimine el objeto y vuelva a lanzar la excepción:
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;
}
Sí, hay una fuga , dependiendo de cómo defina LEAK y cuánto MÁS TARDE quiere decir ...
Si por fuga quiere decir "la memoria permanece asignada, no disponible para su uso, aunque haya terminado de usarla" y al final quiere decir en cualquier momento después de llamar a disponer, entonces sí puede haber una fuga, aunque no es permanente (es decir, la vida de su tiempo de ejecución de aplicaciones).
Para liberar la memoria administrada utilizada por MemoryStream, debe desvincularla anulando su referencia a ella, de modo que sea elegible para la recolección de basura de inmediato. Si no puede hacer esto, entonces crea una fuga temporal desde el momento en que termina de usarla, hasta que su referencia quede fuera del alcance, porque mientras tanto, la memoria no estará disponible para la asignación.
El beneficio de la declaración de uso (más que simplemente llamar a disponer) es que usted puede DECLARAR su referencia en la declaración de uso. Cuando la instrucción using finaliza, no solo se llama a disposer, sino que su referencia queda fuera del alcance, anulando efectivamente la referencia y haciendo que su objeto sea elegible para recolección de basura inmediatamente sin que tenga que recordar escribir el código "reference = null".
Si bien no dejar de hacer referencia a algo de inmediato no es una pérdida de memoria clásica "permanente", definitivamente tiene el mismo efecto. Por ejemplo, si mantiene su referencia al MemoryStream (incluso después de realizar una llamada a dispose), y un poco más abajo en su método, intenta asignar más memoria ... la memoria en uso por su secuencia de memoria aún referenciada no estará disponible para usted hasta que anule la referencia o se sale del alcance, a pesar de que llamó a disponer y ya lo hizo.