net collector c# .net garbage-collection

collector - gc c#



La mejor práctica para forzar la recolección de basura en C# (15)

En mi experiencia, parece que la mayoría de la gente te dirá que no es prudente forzar una recolección de basura, pero en algunos casos donde trabajas con objetos grandes que no siempre se recopilan en la generación 0 pero donde la memoria es un problema, es está bien forzar la recolección? ¿Existe una mejor práctica para hacerlo?


No estoy seguro si es una mejor práctica ...

Sugerencia: no implemente esto ni nada cuando no esté seguro. Reevaluar cuando se conocen los hechos, luego realizar pruebas de rendimiento antes / después para verificar.


Sin embargo, si puede probar su código de manera confiable para confirmar que llamar a Collect () no tendrá un impacto negativo, continúe ...

En mi humilde opinión, esto es similar a decir "Si puedes probar que tu programa nunca tendrá ningún error en el futuro, entonces adelante ..."

Con toda seriedad, forzar el GC es útil para depurar / probar. Si sientes que necesitas hacerlo en otro momento, entonces estás equivocado o tu programa ha sido creado incorrectamente. De cualquier manera, la solución no está forzando al GC ...


Aprendí a no intentar ser más astuto que la recolección de basura. Dicho esto, me limito a utilizar la palabra clave cuando se trata de recursos no administrados como E / S de archivos o conexiones de bases de datos.


Creo que el ejemplo de Rico Mariani fue bueno: puede ser apropiado activar un GC si hay un cambio significativo en el estado de la aplicación. Por ejemplo, en un editor de documentos, puede estar bien activar un GC cuando se cierra un documento.


Creo que ya mencionó la mejor práctica y NO es usarla a menos que sea REALMENTE necesario. Recomiendo encarecidamente examinar su código con más detalle, utilizando potencialmente herramientas de creación de perfiles si es necesario para responder estas preguntas primero.

  1. ¿Tiene algo en su código que declara los artículos en un alcance más grande que el necesario
  2. Es el uso de la memoria realmente demasiado alto
  3. Compare el rendimiento antes y después de usar GC.Collect () para ver si realmente ayuda.

Hay algunas pautas generales en la programación que son absolutas. La mitad del tiempo, cuando alguien dice ''lo estás haciendo mal'', solo están emitiendo una cierta cantidad de dogmas. En C, solía haber miedo a cosas como el código de modificación automática o los hilos, en los lenguajes del GC está forzando al GC o, alternativamente, impidiendo que el GC se ejecute.

Como es el caso con la mayoría de las pautas y buenas reglas generales (y buenas prácticas de diseño), hay raras ocasiones en las que tiene sentido trabajar en torno a la norma establecida. Tienes que estar seguro de que entiendes el caso, que tu caso realmente requiere la abrogación de la práctica común, y que entiendes los riesgos y efectos secundarios que puedes causar. Pero hay tales casos.

Los problemas de programación son muy variados y requieren un enfoque flexible. He visto casos en los que tiene sentido bloquear el GC en los lenguajes y lugares recogidos de basura donde tiene sentido desencadenarlo en lugar de esperar que ocurra de forma natural. El 95% del tiempo, cualquiera de estos sería una señal de no haber abordado correctamente el problema. Pero 1 vez en 20, probablemente haya un caso válido para eso.


La mejor práctica es no forzar una recolección de basura.

De acuerdo con MSDN:

"Es posible forzar la recolección de basura llamando a Collect, pero la mayoría de las veces, esto debe evitarse porque puede crear problemas de rendimiento".

Sin embargo, si puede probar su código de manera confiable para confirmar que llamar a Collect () no tendrá un impacto negativo, continúe ...

Solo intenta asegurarte de que los objetos se limpien cuando ya no los necesites. Si tiene objetos personalizados, mire usando la "declaración de uso" y la interfaz IDisposable.

Este enlace tiene algunos buenos consejos prácticos con respecto a la liberación de memoria / recolección de basura, etc.

http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx


Los objetos grandes se asignan en LOH (gran pila de objetos), no en gen 0. Si estás diciendo que no se recogen basura con gen 0, tienes razón. Creo que solo se recopilan cuando ocurre el ciclo completo del GC (generaciones 0, 1 y 2).

Una vez dicho esto, creo que, por otro lado, el GC se ajustará y recolectará la memoria de forma más agresiva cuando se trabaja con objetos grandes y la presión de la memoria aumenta.

Es difícil decir si recopilar o no y en qué circunstancias. Solía ​​hacer GC.Collect () después de eliminar ventanas / formularios de diálogo con numerosos controles, etc. (porque para cuando el formulario y sus controles terminan en gen 2 debido a la creación de muchas instancias de objetos comerciales / carga de muchos datos, no Objetos grandes, obviamente), pero en realidad no notó ningún efecto positivo o negativo a largo plazo al hacerlo.


Mírelo de esta manera: ¿es más eficaz tirar la basura de la cocina cuando la basura está al 10% o dejar que se llene antes de sacarla?

Al no dejar que se llene, usted está perdiendo el tiempo caminando hacia y desde el cubo de basura afuera. Esto es análogo a lo que sucede cuando se ejecuta el subproceso del GC: todos los subprocesos administrados se suspenden mientras se está ejecutando. Y si no me equivoco, el hilo del GC se puede compartir entre varios AppDomains, por lo que la recolección de elementos no utilizados los afecta a todos.

Por supuesto, es posible que encuentre una situación en la que no agregue nada al basurero en el corto plazo, por ejemplo, si va a tomar unas vacaciones. Entonces, sería una buena idea tirar la basura antes de salir.

Esto PODRÍA ser una vez que forzar un GC puede ayudar: si su programa está inactivo, la memoria en uso no se recoge basura porque no hay asignaciones.


Me gustaría agregar que: Llamar a GC.Collect () (+ WaitForPendingFinalizers ()) es una parte de la historia. Como otros mencionaron correctamente, GC.COllect () es una recopilación no determinista y se deja a discreción del propio GC (CLR). Incluso si agrega una llamada a WaitForPendingFinalizers, puede que no sea determinista. Tome el código de este link msdn y ejecute el código con la iteración del bucle del objeto como 1 o 2. Encontrará lo que significa no determinista (establecer un punto de ruptura en el destructor del objeto). Precisamente, no se llama al destructor cuando solo había 1 (o 2) objetos persistentes por Wait .. (). [Citation reqd.]

Si su código trata con recursos no administrados (por ejemplo, identificadores de archivos externos), debe implementar destructores (o finalizadores).

Aquí hay un ejemplo interesante:

Nota : Si ya ha probado el ejemplo anterior de MSDN, el siguiente código borrará el aire.

class Program { static void Main(string[] args) { SomePublisher publisher = new SomePublisher(); for (int i = 0; i < 10; i++) { SomeSubscriber subscriber = new SomeSubscriber(publisher); subscriber = null; } GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine(SomeSubscriber.Count.ToString()); Console.ReadLine(); } } public class SomePublisher { public event EventHandler SomeEvent; } public class SomeSubscriber { public static int Count; public SomeSubscriber(SomePublisher publisher) { publisher.SomeEvent += new EventHandler(publisher_SomeEvent); } ~SomeSubscriber() { SomeSubscriber.Count++; } private void publisher_SomeEvent(object sender, EventArgs e) { // TODO: something string stub = ""; } }

Sugiero que primero analice cuál puede ser el resultado y luego ejecute y luego lea el siguiente motivo:

{El destructor solo se llama implícitamente una vez que finaliza el programa. } Para limpiar el objeto de forma determinista, se debe implementar IDisposable y realizar una llamada explícita a Dispose (). Esa es la esencia! :)


No estoy seguro de si es una buena práctica, pero cuando trabajo con grandes cantidades de imágenes en un bucle (es decir, creando y eliminando una gran cantidad de objetos de Gráficos / Imagen / Mapa de bits), regularmente dejo que GC.Collect.

Creo que leí en alguna parte que el GC solo se ejecuta cuando el programa está (en su mayoría) inactivo, y no en el medio de un bucle intensivo, por lo que podría verse como un área donde el GC manual podría tener sentido.


Recientemente encontré un caso que requería llamadas manuales a GC.Collect() cuando trabajaba con objetos grandes de C ++ que estaban envueltos en pequeños objetos administrados de C ++, que a su vez se accedían desde C #.

El recolector de basura nunca se llamó porque la cantidad de memoria administrada utilizada era insignificante, pero la cantidad de memoria no administrada utilizada era enorme. Llamar manualmente a Dispose() sobre los objetos requeriría que GC.Collect() un seguimiento de cuándo los objetos ya no son necesarios, mientras que llamar a GC.Collect() limpiará cualquier objeto que ya no se mencione ...


Supongamos que su programa no tiene pérdida de memoria, los objetos se acumulan y no pueden ser GC-ed en Gen 0 porque: 1) Se les hace referencia durante mucho tiempo, así que entre en Gen1 y Gen2; 2) Son objetos grandes (> 80K), así que entra en LOH (Large Object Heap). Y LOH no hace compactación como en Gen0, Gen1 y Gen2.

Verifique el contador de rendimiento de ".NET Memory" puede ver que el 1) problema realmente no es un problema. En general, cada 10 Gen0 GC disparará 1 Gen1 GC, y cada 10 Gen1 GC activará 1 Gen2 GC. Teóricamente, GC1 y GC2 nunca pueden ser GC-ed si no hay presión en GC0 (si el uso de la memoria del programa está realmente cableado). Nunca me pasa a mí.

Para el problema 2), puede verificar el contador de rendimiento ".NET Memory" para verificar si LOH se está hinchando. Si realmente es un problema para su problema, quizás pueda crear un grupo de objetos grandes ya que este blog sugiere http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx .


Una cosa más, activar GC Collect explícitamente NO puede mejorar el rendimiento de su programa. Es bastante posible empeorar las cosas.

El .NET GC está bien diseñado y ajustado para ser adaptable, lo que significa que puede ajustar el umbral de GC0 / 1/2 de acuerdo con el "hábito" del uso de memoria de su programa. Por lo tanto, se adaptará a su programa después de algún tiempo de ejecución. Una vez que invoque GC.Collect explícitamente, los umbrales se restablecerán. Y .NET tiene que pasar tiempo para adaptarse al "hábito" de su programa nuevamente.

Mi sugerencia es siempre confiar en .NET GC. Cualquier superficie con problemas de memoria, verifique el contador de rendimiento ".NET Memory" y diagnostique mi propio código.


La mejor práctica es no forzar una recolección de basura en la mayoría de los casos. (Todos los sistemas en los que he trabajado que forzaron recolecciones de basura tenían problemas subyacentes que, de resolverse, habrían eliminado la necesidad de forzar la recolección de basura y acelerarían mucho el sistema).

Hay algunos casos en los que sabe más sobre el uso de memoria que el recolector de elementos no utilizados. Es poco probable que esto sea cierto en una aplicación multiusuario o en un servicio que responde a más de una solicitud a la vez.

Sin embargo, en algunos procesos de tipo por lotes, usted sabe más que el GC. Por ejemplo, considere una aplicación que.

  • Se le da una lista de nombres de archivos en la línea de comando
  • Procesa un solo archivo y luego escribe el resultado en un archivo de resultados.
  • Al procesar el archivo, crea una gran cantidad de objetos interconectados que no se pueden recopilar hasta que el procesamiento del archivo se haya completado (por ejemplo, un árbol de análisis sintáctico)
  • No mantiene el estado de coincidencia entre los archivos que ha procesado .

Es posible que pueda realizar una prueba de caso (después de la cuidadosa) de que debe forzar una recolección completa de basura después de procesar cada archivo.

Otro caso es un servicio que se activa cada pocos minutos para procesar algunos elementos y no mantiene ningún estado mientras está dormido . Entonces, forzar una colección completa justo antes de ir a dormir puede valer la pena.

La única vez que consideraría forzar una colección es cuando sé que se crearon muchos objetos recientemente y que actualmente se hace referencia a muy pocos objetos.

Preferiría tener una API de recolección de basura cuando pudiera darle pistas sobre este tipo de cosas sin tener que forzar una GC.

Ver también " Tidbits de rendimiento de Rico Mariani "