El consejo general es que no debe llamar GC.Collect
desde su código, pero ¿cuáles son las excepciones a esta regla?
Solo puedo pensar en algunos casos muy específicos en los que puede tener sentido forzar una recolección de basura.
Un ejemplo que me viene a la mente es un servicio, que se despierta a intervalos, realiza alguna tarea y luego duerme durante mucho tiempo. En este caso, puede ser una buena idea forzar una recopilación para evitar que el proceso que pronto estará inactivo retenga más memoria de la necesaria.
¿Hay otros casos en los que es aceptable llamar GC.Collect
?
c#
.net
garbage-collection
Brian Rasmussen
fuente
fuente
Respuestas:
Si tiene buenas razones para creer que un conjunto significativo de objetos, particularmente aquellos que sospecha que son de las generaciones 1 y 2, ahora son elegibles para la recolección de basura, y ese sería un momento apropiado para recolectar en términos del pequeño impacto en el rendimiento .
Un buen ejemplo de esto es si acaba de cerrar un formulario grande. Usted sabe que todos los controles de la interfaz de usuario ahora se pueden recolectar basura, y una pausa muy corta a medida que se cierra el formulario probablemente no sea notoria para el usuario.
ACTUALIZACIÓN 2.7.2018
A partir de .NET 4.5, hay
GCLatencyMode.LowLatency
yGCLatencyMode.SustainedLowLatency
. Al entrar y salir de cualquiera de estos modos, se recomienda que fuerce un GC completo conGC.Collect(2, GCCollectionMode.Forced)
.A partir de .NET 4.6, existe el
GC.TryStartNoGCRegion
método (utilizado para establecer el valor de solo lecturaGCLatencyMode.NoGCRegion
). Esto puede, en sí mismo, realizar una recolección de basura de bloqueo completo en un intento de liberar suficiente memoria, pero dado que no permitimos GC durante un período, diría que también es una buena idea realizar GC completo antes y después.Fuente: ingeniero de Microsoft Ben Watson: escritura de código .NET de alto rendimiento , 2ª ed. 2018.
Ver:
fuente
GC.Collect
, básicamente le estás diciendo al recolector de basura que sabes mejor que para un cambio. ¿Por qué no te gusta el ejemplo?GC.Collect()
solicitará que el GC realice una recopilación completa . Si sabe que acaba de hacer que muchos objetos de larga vida anterior sean elegibles para la recolección de basura y cree que es menos probable que el usuario note una pequeña pausa ahora que más tarde, parece completamente razonable pensar que ahora es mejor hora de solicitar una colección que dejar que suceda más tarde.Solo lo uso
GC.Collect
al escribir plataformas de prueba de rendimiento / perfilador de crudo; es decir, tengo dos (o más) bloques de código para probar, algo como:De manera que
TestA()
yTestB()
ejecutar la mayor estado similar como sea posible - es decir,TestB()
no consigue martillado sólo porqueTestA
lo dejó muy cerca del punto de inflexión.Un ejemplo clásico sería un simple exe de consola (un
Main
método lo suficientemente ordenado para ser publicado aquí, por ejemplo), que muestra la diferencia entre la concatenación de cadenas en bucle yStringBuilder
.Si necesito algo preciso, entonces serían dos pruebas completamente independientes, pero a menudo esto es suficiente si solo queremos minimizar (o normalizar) el GC durante las pruebas para tener una idea aproximada del comportamiento.
Durante el código de producción? Todavía tengo que usarlo ;-p
fuente
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 la recolección de basura, tuvieron problemas que subrayaron que si se resuelven habrían eliminado la necesidad de forzar la recolección de basura, y aceleraron mucho el sistema).
Hay algunos casos en los que sabe más sobre el uso de la memoria que el recolector de basura. 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 tipos de procesamiento por lotes, usted sabe más que el GC. Por ejemplo, considere una aplicación que.
Es posible que pueda hacer un caso (después de una cuidadosa) prueba de que debe forzar una recolección de basura completa después de haber procesado 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 irse a dormir puede valer la pena.
Preferiría tener una API de recolección de basura cuando pudiera darle pistas sobre este tipo de cosas sin tener que forzar un GC por mí mismo.
Ver también " Tidbits de rendimiento de Rico Mariani "
fuente
Un caso es cuando intenta unificar el código de prueba que usa WeakReference .
fuente
En sistemas grandes 24/7 o 24/6, sistemas que reaccionan a mensajes, solicitudes RPC o que sondean una base de datos o procesan continuamente, es útil tener una forma de identificar pérdidas de memoria. Para esto, tiendo a agregar un mecanismo a la aplicación para suspender temporalmente cualquier procesamiento y luego realizar la recolección de basura completa. Esto pone al sistema en un estado de reposo donde la memoria restante es legítimamente memoria de larga duración (cachés, configuración, etc.) o se 'filtra' (objetos que no se espera o se desea que se arraiguen, pero en realidad lo están).
Tener este mecanismo hace que sea mucho más fácil perfilar el uso de la memoria, ya que los informes no se verán nublados por el ruido del procesamiento activo.
Para asegurarse de obtener toda la basura, debe realizar dos recolecciones:
Como la primera colección causará que se finalicen los objetos con finalizadores (pero no la recolección de basura en realidad estos objetos). El segundo GC recogerá basura estos objetos finalizados.
fuente
Puede llamar a GC.Collect () cuando sepa algo sobre la naturaleza de la aplicación que el recolector de basura no conoce. Es tentador pensar que, como el autor, esto es muy probable. Sin embargo, la verdad es que el GC equivale a un sistema experto bastante bien escrito y probado, y es raro que sepa algo sobre las rutas de código de bajo nivel que no lo hace.
El mejor ejemplo que puedo pensar sobre dónde podría tener información adicional es una aplicación que alterna entre períodos inactivos y períodos muy ocupados. Desea el mejor rendimiento posible para los períodos de mayor actividad y, por lo tanto, desea utilizar el tiempo de inactividad para hacer una limpieza.
Sin embargo, la mayoría de las veces el GC es lo suficientemente inteligente como para hacer esto de todos modos.
fuente
Como una solución de fragmentación de memoria. Me estaba quedando sin excepciones de memoria mientras escribía muchos datos en una secuencia de memoria (lectura de una secuencia de red). Los datos se escribieron en fragmentos de 8K. Después de alcanzar los 128M hubo una excepción a pesar de que había mucha memoria disponible (pero estaba fragmentada). Llamar a GC.Collect () resolvió el problema. Pude manejar más de 1G después de la corrección.
fuente
Echa un vistazo a este artículo de Rico Mariani. Da dos reglas cuando llamar a GC.Collect (la regla 1 es: "No"):
Cuándo llamar a GC.Collect ()
fuente
Una instancia en la que es casi necesario llamar a GC.Collect () es cuando se automatiza Microsoft Office a través de Interop. A los objetos COM para Office no les gusta lanzarse automáticamente y pueden dar lugar a que las instancias del producto de Office consuman grandes cantidades de memoria. No estoy seguro de si esto es un problema o por diseño. Hay muchas publicaciones sobre este tema en Internet, por lo que no entraré en demasiados detalles.
Cuando se programa utilizando Interop, cada objeto COM debe liberarse manualmente, generalmente mediante el uso de Marshal.ReleseComObject (). Además, llamar a Recolección de basura manualmente puede ayudar a "limpiar" un poco. Llamar al siguiente código cuando haya terminado con los objetos de Interop parece ayudar bastante:
En mi experiencia personal, usar una combinación de ReleaseComObject y llamar manualmente a la recolección de basura reduce en gran medida el uso de memoria de los productos de Office, específicamente Excel.
fuente
Estaba haciendo algunas pruebas de rendimiento en la matriz y la lista:
y obtuve el
OutOfMemoryException
método GetSomeNumbers_Enumerable_Range, la única solución es desasignar la memoria mediante:fuente
En su ejemplo, creo que llamar a GC.Collect no es el problema, sino que hay un problema de diseño.
Si va a despertarse a intervalos (tiempos establecidos), entonces su programa debe estar diseñado para una sola ejecución (realizar la tarea una vez) y luego finalizar. Luego, configura el programa como una tarea programada para ejecutarse en los intervalos programados.
De esta manera, no tiene que preocuparse por llamar a GC.Collect (lo cual rara vez debería hacer).
Dicho esto, Rico Mariani tiene una gran publicación de blog sobre este tema, que se puede encontrar aquí:
http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx
fuente
Un lugar útil para llamar a GC.Collect () es en una prueba unitaria cuando desea verificar que no está creando una pérdida de memoria (por ejemplo, si está haciendo algo con WeakReferences o ConditionalWeakTable, código generado dinámicamente, etc.).
Por ejemplo, tengo algunas pruebas como:
Se podría argumentar que usar WeakReferences es un problema en sí mismo, pero parece que si está creando un sistema que se basa en ese comportamiento, entonces llamar a GC.Collect () es una buena manera de verificar dicho código.
fuente
La entrada del blog de Scott Holden sobre cuándo (y cuándo no) llamar a GC.Collect es específico de .NET Compact Framework , pero las reglas generalmente se aplican a todo el desarrollo administrado.
fuente
La respuesta corta es: ¡nunca!
fuente
fuente
Hay algunas situaciones en las que es mejor prevenir que curar.
Aquí hay una situación.
Es posible crear una DLL no administrada en C # usando reescrituras de IL (porque hay situaciones en las que esto es necesario).
Ahora supongamos, por ejemplo, que la DLL crea una matriz de bytes en el nivel de clase, porque muchas de las funciones exportadas necesitan acceso a ellas. ¿Qué sucede cuando se descarga la DLL? ¿Se llama automáticamente al recolector de basura en ese punto? No lo sé, pero al ser una DLL no administrada , es muy posible que no se llame al GC. Y sería un gran problema si no se llamara. Cuando se descarga la DLL, también sería el recolector de basura, entonces, ¿quién será responsable de recolectar cualquier posible basura y cómo lo harían? Es mejor emplear el recolector de basura de C #. Tiene una función de limpieza (disponible para el cliente DLL) donde las variables de nivel de clase se configuran como nulas y se llama al recolector de basura.
Más vale prevenir que lamentar.
fuente
Todavía estoy bastante inseguro sobre esto. Estoy trabajando desde hace 7 años en un servidor de aplicaciones. Nuestras instalaciones más grandes utilizan 24 GB de RAM. Es altamente multiproceso, y TODAS las llamadas para GC.Collect () se encontraron con problemas de rendimiento realmente terribles.
Muchos componentes de terceros usaron GC.Collect () cuando pensaron que era inteligente hacer esto ahora. Entonces, un simple grupo de informes de Excel bloqueó el servidor de aplicaciones para todos los hilos varias veces por minuto.
Tuvimos que refactorizar todos los componentes de terceros para eliminar las llamadas GC.Collect (), y todo funcionó bien después de hacer esto.
Pero también estoy ejecutando servidores en Win32, y aquí comencé a usar GC.Collect () después de obtener una excepción OutOfMemoryException.
Pero también estoy bastante inseguro sobre esto, porque a menudo me di cuenta, cuando obtengo un OOM en 32 Bit, y vuelvo a intentar ejecutar la misma Operación nuevamente, sin llamar a GC.Collect (), simplemente funcionó bien.
Una cosa que me pregunto es la Excepción OOM en sí ... Si hubiera escrito .Net Framework, y no puedo asignar un bloque de memoria, usaría GC.Collect (), defrag memory (??), intente nuevamente , y si todavía no puedo encontrar un bloque de memoria libre, entonces lanzaría la OOM-Exception.
O al menos haga que este comportamiento sea una opción configurable, debido a los inconvenientes del problema de rendimiento con GC.Collect.
Ahora tengo un montón de código como este en mi aplicación para "resolver" el problema:
(Tenga en cuenta que el comportamiento Thread.Sleep () es un comportamiento realmente específico de la aplicación, porque estamos ejecutando un servicio de almacenamiento en caché ORM, y el servicio tarda un tiempo en liberar todos los objetos en caché, si la RAM excede algunos valores predefinidos. unos segundos la primera vez, y ha aumentado el tiempo de espera cada vez que aparece OOM).
fuente
GC.Collect
. Dado que tiene un efecto amplio en la aplicación, solo la aplicación debería hacerlo (si es que lo hace).If i would have written the .Net Framework, and i can't alloc a memory block, i would use GC.Collect(),
- Creo que ya están haciendo esto - He visto indicios de que uno de los desencadenantes internos del GC son ciertas fallas en la asignación de memoria.Debe intentar evitar usar GC.Collect () ya que es muy costoso. Aquí hay un ejemplo:
RESULTADO DE LA PRUEBA: USO DE LA CPU 12%
Cuando cambias a esto:
RESULTADO DE LA PRUEBA: USO DE LA CPU 2-3%
fuente
Otra razón es cuando tiene un SerialPort abierto en un puerto USB COM, y luego el dispositivo USB se desconecta. Debido a que se abrió SerialPort, el recurso contiene una referencia al puerto previamente conectado en el registro del sistema. El registro del sistema entonces contendrá datos obsoletos , por lo que la lista de puertos disponibles será incorrecta. Por lo tanto, el puerto debe estar cerrado.
Llamar a SerialPort.Close () en el puerto llama a Dispose () en el objeto, pero permanece en la memoria hasta que la recolección de basura realmente se ejecute, lo que hace que el registro permanezca obsoleto hasta que el recolector de basura decida liberar el recurso.
Desde https://stackoverflow.com/a/58810699/8685342 :
fuente
Esto no es tan relevante para la pregunta, pero para las transformaciones XSLT en .NET (XSLCompiledTranform), es posible que no tenga otra opción. Otro candidato es el control MSHTML.
fuente
Si está utilizando una versión de .net inferior a 4.5, la recopilación manual puede ser inevitable (especialmente si se trata de muchos 'objetos grandes').
Este enlace describe por qué:
https://blogs.msdn.microsoft.com/dotnet/2011/10/03/large-object-heap-improvements-in-net-4-5/
fuente
Una buena razón para llamar a GC es en computadoras ARM pequeñas con poca memoria, como Raspberry PI (que se ejecuta con mono). Si los fragmentos de memoria no asignados utilizan demasiada RAM del sistema, el sistema operativo Linux puede volverse inestable. Tengo una aplicación donde tengo que llamar a GC cada segundo (!) Para deshacerme de los problemas de desbordamiento de memoria.
Otra buena solución es deshacerse de los objetos cuando ya no son necesarios. Lamentablemente, esto no es tan fácil en muchos casos.
fuente
Como hay montón de objetos pequeños (SOH) y montón de objetos grandes (LOH)
Podemos llamar a GC.Collect () para borrar el objeto de desreferencia en SOP y mover el objeto vivido a la próxima generación.
En .net4.5, también podemos compactar LOH usando el modo de compactación de objetos grandes
fuente
Si está creando muchos
System.Drawing.Bitmap
objetos nuevos , el recolector de basura no los borra. Finalmente, GDI + pensará que se está quedando sin memoria y lanzará una excepción "El parámetro no es válido". Llamar deGC.Collect()
vez en cuando (¡no con demasiada frecuencia!) Parece resolver este problema.fuente