Pregunta

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 no se pueda eliminar más tarde?

Tengo una revisión por pares que insiste en que cierro esto manualmente, y no puedo encontrar la información para saber si tiene un punto válido o no.

¿Fue útil?

Solución

Si algo es desechable, siempre debe desecharlo. Debería usar una instrucción de uso en su método bar () para asegurarse de que ms2 se elimine.

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ía como una advertencia.

Otros consejos

No perderá nada, al menos en la implementación actual.

Calling Dispose no limpiará la memoria utilizada por MemoryStream más rápido. hará que su flujo deje de ser viable para las llamadas de Lectura / Escritura después de la llamada, lo que puede o no serle útil.

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, generalmente es una buena práctica en parte porque si alguna vez haces cambias para usar un Stream diferente, no querrás que te pique un error difícil de encontrar porque escogiste la salida fácil antes de tiempo. en. (Por otro lado, está el argumento 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.

Sí, hay una fuga , dependiendo de cómo defina LEAK y cuánto MÁS TARDE quiera decir ...

Si por fuga quiere decir "la memoria permanece asignada, no está disponible para su uso, aunque haya terminado de usarla" y con esto último quiere decir en cualquier momento después de llamar a dispose, entonces sí, puede haber una fuga, aunque no es permanente (es decir, durante la vida útil de sus aplicaciones).

Para liberar la memoria administrada utilizada por MemoryStream, necesita desreferenciarla , anulando su referencia a ella, para que sea elegible para la recolección de basura de inmediato. Si no lo hace, crea una fuga temporal desde el momento en que termina de usarla, hasta que su referencia queda fuera de alcance, porque mientras tanto la memoria no estará disponible para la asignación.

El beneficio de la declaración de uso (simplemente llamando a dispose) es que puede DECLARAR su referencia en la declaración de uso. Cuando finaliza la instrucción de uso, no solo se llama a dispose, sino que su referencia queda fuera de alcance, anulando efectivamente la referencia y haciendo que su objeto sea elegible para la recolección de basura de inmediato, sin requerir que recuerde escribir la " reference = null " código.

Si bien no eliminar la referencia de algo de inmediato no es un clásico "permanente" Fuga de memoria, definitivamente tiene el mismo efecto. Por ejemplo, si mantiene su referencia a MemoryStream (incluso después de llamar a dispose), y un poco más abajo en su método, intenta asignar más memoria ... la memoria en uso por su flujo de memoria aún referenciado no estará disponible a usted hasta que anule la referencia o se salga del alcance, incluso aunque haya llamado a disponer y haya terminado de usarla.

Esto ya está respondido, pero solo agregaré que el buen principio de la ocultación de información significa que es posible que en algún momento futuro quiera refactorizar:

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 importa qué tipo de Stream se está devolviendo, y hace posible cambiar la implementación interna (por ejemplo, cuando se burlan de las pruebas unitarias).

Entonces, tendrá que estar en problemas si no ha utilizado la aplicación Dispose en su barra:

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

Todas las secuencias implementan IDisposable. Envuelva su flujo de memoria en una declaración usando y estará bien y elegante. El bloque de uso garantizará que su transmisión esté cerrada y eliminada sin importar qué.

dondequiera que llames a Foo, puedes hacerlo usando (MemoryStream ms = foo ()) y creo que todavía deberías estar bien.

No se requiere llamar a .Dispose () (o envolver con Uso ).

La razón por la que llama a .Dispose () es para liberar el recurso lo antes posible .

Piense en términos de, digamos, el servidor de desbordamiento de pila, donde tenemos un conjunto limitado de memoria y miles de solicitudes. No queremos esperar a la recolección de basura programada, queremos liberar esa memoria lo antes posible por lo que está disponible para nuevas solicitudes entrantes.

No perderá memoria, pero su revisor de código es correcto para indicar que debe cerrar su transmisión. Es cortés hacerlo.

La única situación en la que puede perder memoria es cuando accidentalmente deja una referencia a la secuencia y nunca la cierra. Todavía no estás perdiendo realmente la memoria, pero estás ampliando innecesariamente el tiempo que dices que lo estás utilizando.

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 pueda, o usted (o alguien más en su empresa) pueda reemplazarlo con su propio MemoryStream personalizado, etc.
  • Ayuda a establecer un patrón en su proyecto para garantizar que se eliminen todas secuencias: la línea se dibuja más firmemente al decir "todas las secuencias deben eliminarse"; en lugar de " algunos Streams deben eliminarse, pero algunos no tienen que " ...
  • Si alguna vez cambia el código para permitir la devolución de otros tipos de Streams, deberá cambiarlo para eliminarlo de todos modos.

Otra cosa que suelo hacer en casos como foo () al crear y devolver un IDisposible es garantizar que cualquier falla entre la construcción del objeto y el return sea detectada por una excepción, dispone el objeto y vuelve 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;
}

Si un objeto implementa IDisposable, debe llamar al método .Dispose cuando haya terminado.

En algunos objetos, Dispose significa lo mismo que Close y viceversa, en ese caso, cualquiera de los dos es bueno.

Ahora, para su pregunta particular, no, no perderá memoria.

No soy un experto en .net, pero quizás el problema aquí son los recursos, es decir, el identificador de archivo y no la memoria. Supongo que el recolector de basura eventualmente liberará el flujo y cerrará el manejador, pero creo que siempre sería una buena práctica cerrarlo explícitamente, para asegurarse de que eliminas el contenido en el disco.

La eliminación de recursos no administrados no es determinista en los idiomas de recolección de basura. Incluso si llama a Dispose explícitamente, no tiene absolutamente ningún control sobre cuándo se libera realmente la memoria de respaldo. Dispose se llama implícitamente cuando un objeto se sale del alcance, ya sea al salir de una instrucción using o al abrir la pila de llamadas desde un método subordinado. Dicho todo esto, a veces el objeto puede en realidad ser un envoltorio para un recurso administrado (por ejemplo, un archivo). Esta es la razón por la que es una buena práctica cerrar explícitamente en las declaraciones finalmente o usar la instrucción using. Saludos

MemorySteram no es más que una matriz de bytes, que es un objeto gestionado. Olvídese de desechar o cerrar, esto no tiene ningún efecto secundario más que sobre la finalización.
Simplemente verifique el método de funcionamiento o de lavado de MemoryStream en el reflector y quedará claro por qué no necesita preocuparse por cerrarlo o desecharlo de otra manera que no sea por seguir una buena práctica.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top