Aunque entiendo las serias implicaciones de jugar con esta función (o al menos eso es lo que creo), no veo por qué se está convirtiendo en una de estas cosas que los programadores respetables nunca usarían, incluso aquellos que ni siquiera saben para qué sirve.
Digamos que estoy desarrollando una aplicación en la que el uso de la memoria varía enormemente según lo que esté haciendo el usuario. El ciclo de vida de la aplicación se puede dividir en dos etapas principales: edición y procesamiento en tiempo real. Durante la etapa de edición, suponga que se crean miles de millones o incluso billones de objetos; algunos de ellos pequeños y otros no, algunos pueden tener finalizadores y otros no, y suponen que su vida útil varía desde unos pocos milisegundos hasta largas horas. A continuación, el usuario decide cambiar a la etapa de tiempo real. En este punto, suponga que el desempeño juega un papel fundamental y la más mínima alteración en el flujo del programa podría traer consecuencias catastróficas. La creación de objetos se reduce al mínimo posible mediante el uso de grupos de objetos y todo eso, pero luego, el GC interviene inesperadamente y lo tira todo, y alguien muere.
La pregunta: En este caso, ¿no sería prudente llamar a GC.Collect () antes de ingresar a la segunda etapa?
Después de todo, estas dos etapas nunca se superponen en el tiempo entre sí y toda la optimización y las estadísticas que el GC podría haber recopilado serían de poca utilidad aquí ...
Nota: Como algunos de ustedes han señalado, .NET podría no ser la mejor plataforma para una aplicación como esta, pero eso está más allá del alcance de esta pregunta. La intención es aclarar si una llamada GC.Collect () puede mejorar el comportamiento / rendimiento general de una aplicación o no. Todos estamos de acuerdo en que las circunstancias bajo las cuales harías tal cosa son extremadamente raras pero, de nuevo, el GC intenta adivinar y lo hace perfectamente bien la mayor parte del tiempo, pero aún se trata de adivinar.
Gracias.
fuente
Respuestas:
Del Blog de Rico ...
Entonces, parece que esta situación puede caer bajo la Regla n. ° 2, usted sabe que hay un momento en el tiempo en el que muchos objetos viejos han muerto y no es recurrente. Sin embargo, no olvide las palabras de despedida de Rico.
Mide, mide, mide.
fuente
Si llama a GC.Collect () en el código de producción, esencialmente está declarando que sabe más que los autores del GC. Ese puede ser el caso. Sin embargo, por lo general no lo es y, por lo tanto, se desaconseja enfáticamente.
fuente
malloc
solo tiene una interfaz muy simple, y sus implementaciones pueden variar lo suficiente como para importar. Todo depende del tipo de problema que intente resolver. Simalloc
fuera "suficientemente bueno", no necesitaría la agrupación de búfer, ¿verdad? El desarrollo de C / C ++ está lleno de ejemplos en los que intenta adivinar el sistema operativo / tiempo de ejecución / bibliotecas porque lo sabe mejor (y, a veces, realmente lo sabe). Muchas aplicaciones críticas para el rendimiento evitan por completo el uso de asignadores de tiempo de ejecución / sistema. Juegos utilizados para preasignar toda la memoria al inicio (matrices de tamaño constante, etc.).Entonces, ¿qué pasa cuando usa objetos COM como MS Word o MS Excel de .NET? Sin llamar
GC.Collect
después de liberar los objetos COM, hemos encontrado que las instancias de la aplicación Word o Excel todavía existen.De hecho, el código que usamos es:
Entonces, ¿sería un uso incorrecto del recolector de basura? Si es así, ¿cómo conseguimos que mueran los objetos de Interop? Además, si no está destinado a usarse así, ¿por qué
GC
'sCollect
método aúnPublic
?fuente
Bueno, la GC es una de esas cosas con las que tengo una relación de amor / odio. Lo hemos roto en el pasado a través de VistaDB y hemos escrito un blog al respecto. Lo han arreglado, pero lleva MUCHO tiempo obtener arreglos de ellos en cosas como esta.
El GC es complejo, y un enfoque único para todos es muy, muy difícil de lograr en algo tan grande. MS ha hecho un trabajo bastante bueno, pero a veces es posible engañar al GC.
En general, no debe agregar a a
Collect
menos que sepa con certeza que acaba de deshacerse de una tonelada de memoria y llegará a una crisis de la mediana edad. si el GC no se limpia ahora.Puede arruinar toda la máquina con una serie de malas
GC.Collect
declaraciones. La necesidad de una declaración de recopilación casi siempre apunta a un error subyacente mayor. La pérdida de memoria generalmente tiene que ver con referencias y una falta de comprensión de cómo funcionan. O usarIDisposable
objetos que no lo necesitan y poner una carga mucho mayor en el GC.Observe de cerca el% de tiempo dedicado a GC a través de los contadores de rendimiento del sistema. Si ve que su aplicación usa el 20% o más de su tiempo en el GC, tiene problemas graves de administración de objetos (o un patrón de uso anormal). Desea minimizar siempre el tiempo que gasta el GC porque acelerará toda su aplicación.
También es importante tener en cuenta que el GC es diferente en los servidores que en las estaciones de trabajo. He visto una serie de pequeños problemas difíciles de rastrear con personas que no los prueban a ambos (o que ni siquiera saben que hay dos).
Y solo para ser lo más completo posible en mi respuesta, también debe probar en Mono si también está apuntando a esa plataforma. Dado que es una implementación totalmente diferente, puede experimentar problemas totalmente diferentes a los de la implementación de MS.
fuente
Hay situaciones en las que es útil, pero en general conviene evitarlo. Podrías compararlo con GOTO o con un ciclomotor: lo haces cuando lo necesitas, pero no se lo cuentas a tus amigos.
fuente
Desde mi experiencia, nunca ha sido aconsejable realizar una llamada a GC.Collect () en el código de producción. En la depuración, sí, tiene sus ventajas para ayudar a aclarar posibles fugas de memoria. Supongo que mi razón fundamental es que el GC ha sido escrito y optimizado por programadores mucho más inteligentes que yo, y si llego a un punto en el que siento que necesito llamar a GC.Collect () es una pista de que me he salido del camino algun lado. En su situación, no parece que realmente tenga problemas de memoria, solo que le preocupa la inestabilidad que traerá la recopilación a su proceso. Dado que no limpiará los objetos que todavía están en uso y que se adapta muy rápidamente a las demandas tanto crecientes como decrecientes, creo que no tendrá que preocuparse por eso.
fuente
Una de las principales razones para llamar a GC.Collect () es cuando acaba de realizar un evento significativo que crea mucha basura, como lo que describe. Llamar a GC.Collect () puede ser una buena idea aquí; de lo contrario, es posible que el CG no comprenda que fue un evento "único".
Por supuesto, debes perfilarlo y verlo por ti mismo.
fuente
Bueno, obviamente, no debería escribir código con requisitos en tiempo real en lenguajes con recolección de basura en tiempo no real.
En un caso con etapas bien definidas, no hay ningún problema con activar el recolector de basura. Pero este caso es extremadamente raro. El problema es que muchos desarrolladores intentarán usar esto para resolver problemas de papel en un estilo de culto a la carga, y agregarlo indiscriminadamente causará problemas de rendimiento.
fuente
Llamar a GC.Collect () obliga a CLR a realizar un recorrido por la pila para ver si cada objeto se puede liberar de verdad comprobando las referencias. Esto afectará la escalabilidad si la cantidad de objetos es alta y también se sabe que activa la recolección de basura con demasiada frecuencia. Confíe en CLR y deje que el recolector de basura se ejecute solo cuando sea apropiado.
fuente
De hecho, no creo que sea una mala práctica llamar a GC.Collect.
Puede haber casos en los que lo necesitemos. Solo por ejemplo, tengo un formulario que ejecuta un hilo, que a su vez abre diferentes tablas en una base de datos, extrae el contenido de un campo BLOB a un archivo temporal, cifra el archivo, luego lee el archivo en un flujo binario y de nuevo en un BLOB campo en otra tabla.
Toda la operación requiere bastante memoria y no se sabe con certeza la cantidad de filas y el tamaño del contenido del archivo en las tablas.
Solía obtener la excepción OutofMemory a menudo y pensé que sería prudente ejecutar GC.Collect periódicamente en función de una variable de contador. Incremento un contador y cuando se alcanza un nivel específico, se llama a GC para recolectar cualquier basura que pueda haberse formado y para recuperar cualquier memoria perdida debido a pérdidas de memoria imprevistas.
Después de esto, creo que está funcionando bien, ¡al menos sin excepción!
Llamo de la siguiente manera:
fuente
Bajo .net, el tiempo requerido para realizar una recolección de basura está mucho más relacionado con la cantidad de cosas que no son basura que con la cantidad de cosas que lo son. De hecho, a menos que un objeto anule
Finalize
(ya sea explícitamente o mediante el destructor de C #), sea el objetivo de aWeakReference
, se encuentre en el Montón de objetos grandes o sea especial de alguna otra manera relacionada con gc, lo único que identifica la memoria en la que se encuentra como objeto es la existencia de referencias arraigadas a él. De lo contrario, la operación del GC es análoga a tomar de un edificio todo lo que tiene valor y dinamitar el edificio, construir uno nuevo en el sitio del antiguo y poner todos los artículos valiosos en él. El esfuerzo necesario para dinamitar el edificio es totalmente independiente de la cantidad de basura que contenga.En consecuencia, llamando
GC.Collect
puede aumentar la cantidad total de trabajo que tiene que hacer el sistema. Retrasará la ocurrencia de la siguiente colección, pero probablemente hará tanto trabajo inmediatamente como lo hubiera requerido la siguiente colección cuando ocurrió; en el momento en que se habría producido la siguiente recolección, la cantidad total de tiempo dedicado a la recolección habrá sido aproximadamente el mismo queGC.Collect
no se había llamado, pero el sistema habrá acumulado algo de basura, lo que provocará que la recolección posterior se requiera antes de lo queGC.Collect
no se había hecho ha sido llamado.Los momentos que puedo ver
GC.Collect
que son realmente útiles son cuando uno necesita medir el uso de memoria de algún código (ya que las cifras de uso de memoria solo son realmente significativas después de una colección), o perfilar cuál de varios algoritmos es mejor (llamando a GC.Collect () antes de ejecutar cada uno de varios fragmentos de código puede ayudar a garantizar un estado de línea de base consistente). Hay algunos otros casos en los que uno puede saber cosas que el GC no sabe, pero a menos que uno esté escribiendo un programa de un solo subproceso, no hay forma de que uno pueda saber que unaGC.Collect
llamada que ayudaría a las estructuras de datos de un subproceso a evitar la "crisis de la mediana edad "no causaría que los datos de otros subprocesos tuvieran una" crisis de la mediana edad "que de otro modo se habría evitado.fuente
Creación de imágenes en un bucle: incluso si llama a dispose, la memoria no se recupera. Recolección de basura cada vez. Pasé de 1,7 GB de memoria en mi aplicación de procesamiento de fotos a 24 MB y el rendimiento es excelente.
Es absolutamente el momento de llamar a GC.Collect.
fuente
Dispose
se supone que las llamadas liberen la memoria administrada. Parece que no sabe cómo funciona el modelo de memoria en .NET.Tuvimos un problema similar con el recolector de basura que no recolectaba basura y liberaba memoria.
En nuestro programa, estábamos procesando algunas hojas de cálculo de Excel de tamaño modesto con OpenXML. Las hojas de cálculo contenían entre 5 y 10 "hojas" con aproximadamente 1000 filas de 14 columnas.
El programa en un entorno de 32 bits (x86) se bloqueaba con un error de "memoria insuficiente". Logramos que se ejecutara en un entorno x64, pero queríamos una solución mejor.
Encontramos uno.
Aquí hay algunos fragmentos de código simplificados de lo que no funcionó y lo que funcionó cuando se trata de llamar explícitamente al recolector de basura para liberar memoria de los objetos desechados.
Llamar al GC desde el interior de la subrutina no funcionó. La memoria nunca fue recuperada ...
Al mover la llamada de GC fuera del alcance de la subrutina, se recogió la basura y se liberó la memoria.
Espero que esto ayude a otros que están frustrados con la recolección de basura .NET cuando parece ignorar las llamadas a
GC.Collect()
.Paul Smith
fuente
No hay nada de malo en pedir explícitamente una colección. Algunas personas simplemente quieren creer que si es un servicio proporcionado por el proveedor, no lo cuestionen. Ah, ¿y todos esos bloqueos aleatorios en los momentos equivocados de su aplicación interactiva? ¡La próxima versión lo hará mejor!
Dejar que un proceso en segundo plano se ocupe de la manipulación de la memoria significa no tener que lidiar con eso nosotros mismos, es cierto. Pero esto no significa lógicamente que sea mejor para nosotros no lidiar con eso nosotros mismos bajo todas las circunstancias. El GC está optimizado para la mayoría de los casos. Pero esto no significa lógicamente que esté optimizado en todos los casos.
¿Alguna vez ha respondido una pregunta abierta como "cuál es el mejor algoritmo de clasificación" con una respuesta definitiva? Si es así, no toque el GC. Para aquellos de ustedes que pidieron las condiciones, o dieron respuestas tipo 'en este caso', pueden proceder para aprender sobre el GC y cuándo activarlo.
Tengo que decir que he tenido aplicaciones congeladas en Chrome y Firefox que me frustran muchísimo, e incluso entonces, en algunos casos, la memoria crece sin obstáculos, si tan solo aprendieran a llamar al recolector de basura, o me dieran un para que cuando empiece a leer el texto de una página pueda presionarlo y así estar libre de bloqueos durante los próximos 20 minutos.
fuente
Creo que tiene razón sobre el escenario, pero no estoy seguro de la API.
Microsoft dice que en tales casos debe agregar presión de memoria como una pista al GC de que pronto debería realizar una recopilación.
fuente
¿Qué tiene de malo? El hecho de que esté cuestionando el recolector de basura y el asignador de memoria, que entre ellos tienen una idea mucho mayor sobre el uso de memoria real de su aplicación en tiempo de ejecución que usted.
fuente
El deseo de llamar a GC.Collect () generalmente es tratar de encubrir los errores que cometió en otro lugar.
Sería mejor si encuentra dónde se olvidó de desechar las cosas que ya no necesita.
fuente
En pocas palabras, puede crear un perfil de la aplicación y ver cómo estas colecciones adicionales afectan las cosas. Sin embargo, te sugiero que te mantengas alejado a menos que vayas a hacer un perfil. El GC está diseñado para cuidarse solo y, a medida que evoluciona el tiempo de ejecución, pueden aumentar la eficiencia. Usted no quiere un montón de código rondando que puede estropear el trabajo y no poder aprovechar estas mejoras. Existe un argumento similar para usar foreach en lugar de for, es decir, que se pueden agregar mejoras futuras bajo las cubiertas a foreach y su código no tiene que cambiar para aprovechar.
fuente
El .NET Framework en sí nunca fue diseñado para ejecutarse en un entorno en tiempo real. Si realmente necesita procesamiento en tiempo real, puede usar un lenguaje en tiempo real incrustado que no esté basado en .NET o usar .NET Compact Framework que se ejecuta en un dispositivo Windows CE.
fuente
Lo peor que puede hacer es hacer que su programa se congele un poco. Entonces, si le parece bien, hágalo. Por lo general, no es necesario para aplicaciones web o de cliente pesado con mayor interacción del usuario.
He descubierto que a veces los programas con subprocesos de ejecución prolongada o programas por lotes obtendrán la excepción OutOfMemory aunque estén eliminando los objetos correctamente. Uno que recuerdo fue el procesamiento de transacciones de una base de datos de línea de negocio; la otra era una rutina de indexación en un hilo en segundo plano en una aplicación cliente pesada.
En ambos casos, el resultado fue simple: Sin GC. Recolectar, sin memoria, de manera consistente; GC.Collect, rendimiento impecable.
Lo he intentado para resolver problemas de memoria varias veces, sin éxito. Lo saqué.
En resumen, no lo coloque a menos que obtenga errores. Si lo coloca y no soluciona el problema de memoria, retírelo. Recuerde probar en el modo de lanzamiento y comparar manzanas con manzanas.
El único momento en que las cosas pueden salir mal con esto es cuando te vuelves moralista al respecto. No es una cuestión de valores; muchos programadores han muerto y se han ido directamente al cielo con muchos GC.Collects innecesarios en su código, que les sobrevive.
fuente