.net - problemática - recoleccion de basura definicion
¿Qué desencadena una recolección de basura gen2? (2)
Tengo una situación extraña que estoy tratando de resolver.
El Génesis:
Estoy ejecutando mi programa en una máquina física con 16 núcleos y 128 GB de RAM. Estoy tratando de determinar por qué no usa todos los núcleos disponibles, por lo general usa un 20-25% de CPU en promedio (de 4 a 5 núcleos de los 16). Cuando miro los contadores de rendimiento, se muestran en el orden del 60-70% de tiempo en la recolección de basura.
Para referencia, estoy usando .NET Framework 4 y el TPL (Parallel.ForEach) para enlazar la parte de mi programa que requiere un mayor rendimiento. Estoy limitando el número de hilos a la cantidad de núcleos.
El problema:
Estaba creando una gran cantidad de objetos, demasiados para que el recolector de basura los maneje de manera eficiente y, por lo tanto, pasara una gran cantidad de tiempo en el recolector de basura.
La solución simple hasta el momento:
Estoy introduciendo la agrupación de objetos para reducir la presión en el recolector de basura. Continuaré agrupando objetos para mejorar el rendimiento, ya agrupando algunos objetos reduciendo la recolección de basura del 60-70% del tiempo al 45% del tiempo y mi programa se ejecutó un 40% más rápido.
La pregunta que no responde (la que espero que respondas por mí):
Mi programa cuando se ejecuta utiliza como máximo 14 GB de RAM disponible, en comparación con 128 GB de RAM, esto es bastante pequeño. No hay nada más funcionando en esta máquina (es puramente un banco de pruebas para mí) y hay mucha memoria RAM disponible.
- Si hay suficiente RAM disponible, ¿por qué se está generando alguna colección gen2 (o completa)? Un número bastante grande de estas colecciones gen2 (en los miles) están ocurriendo. es decir, ¿cómo está determinando el umbral para iniciar una colección gen2?
- ¿Por qué el recolector de basura no retrasa las colecciones completas hasta que la presión en la RAM física alcanza un umbral más alto?
- ¿Hay alguna forma en que pueda configurar el recolector de basura para esperar un umbral más alto? (es decir, no molestar en recoger si no es necesario)
EDITAR:
Ya estoy usando la opción para usar el recolector de basura del servidor ... lo que necesito saber es qué es lo que está activando una colección gen2, no que el recolector de basura del servidor sea mejor (ya lo sé).
Como recuerdo, el cliente GC es el predeterminado. Mi experiencia con esto es que no permite que el montón se haga muy grande antes de recolectar. Para mis aplicaciones de procesamiento de trabajo pesado, uso el "servidor" GC.
Habilita el servidor GC en su archivo de configuración de la aplicación:
<?xml version ="1.0"?>
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
Eso hace una gran diferencia en el rendimiento para mí. Por ejemplo, uno de mis programas estaba gastando más del 80% de su tiempo en la recolección de basura. Al habilitar el GC del servidor, eso se redujo a poco más del 10%. El uso de la memoria aumentó porque el GC lo dejó, pero está bien para la mayoría de mis aplicaciones.
Otra cosa que causará una colección Gen 2 es el montón de objetos grandes. Ver CLR Inside Out: montón de objetos grandes descubierto . En pocas palabras, si excede el umbral de LOH, se activará una colección Gen 2. Si está asignando muchos objetos grandes de corta duración (alrededor de 85 kilobytes), esto será un problema.
De memoria vaga y lectura: http://msdn.microsoft.com/en-us/library/ee787088.aspx , creo que un desencadenante de un Gen 2 GC puede ser un segmento de Gen 2 que se llena. El artículo indica que el Servidor GC usa segmentos más grandes, por lo que como ya se indicó, esto probablemente sea importante para su desempeño.
Hacer que la máquina espere hasta que prácticamente no tenga memoria libre significará que en algún momento obtendrás un gran GC. Esto probablemente no es lo ideal. Si su tiempo en GC es tan alto, es una señal de que está asignando demasiados objetos que están sobreviviendo el tiempo suficiente para superar las generaciones 0 y 1, y hacerlo de manera repetitiva. Si el uso de la memoria de su aplicación no aumenta con el tiempo, esto indica que estos objetos son realmente de corta duración, pero viven el tiempo suficiente para sobrevivir a una colección de 0 y de 1. Esta es una mala situación: está asignando un objeto de corta duración pero pagando un costo total de recolección de Gen 2 para limpiarlo.
Si ese es el caso, tienes algunas direcciones diferentes que tomar:
- Trate de que los objetos de vida corta sean coleccionables antes (para que no lleguen a la generación 2 y, por lo tanto, el costo del GC es menor)
- Trate de asignar menos objetos de corta duración (por lo tanto, los GC suceden con menos frecuencia y tiene más tiempo para terminar de usar sus objetos de corta duración antes de que las asignaciones obliguen a un GC y los objetos se muevan a las generaciones anteriores)
- Utilice tipos de valores asignados a la pila en lugar de tipos de referencia para los objetos de vida corta (si se ajusta a su propósito)
- Si sabe que necesita una gran parte de estos objetos, agrúpelos por adelantado. Parece que estás haciendo esto, pero aún debe haber una gran cantidad de asignaciones para mantener el GC en un 45%. Si su grupo no es lo suficientemente grande, asigne más por adelantado, como dice, tiene mucha memoria de repuesto.
Es probable que una combinación de todos estos sea una buena solución. Debe tener una buena comprensión de qué objetos está asignando, cuánto tiempo están viviendo y cuánto tiempo realmente necesitan vivir para cumplir su propósito.
El GC está contento con los objetos temporales que tienen vidas cortas (como se pueden coleccionar rápidamente por el GC), o los objetos a largo plazo / permanentes que tienen vidas largas. La asignación de muchos objetos en medio de estas dos categorías es donde se obtiene el dolor. Así que asigna menos o cambia sus vidas para que coincidan con su escenario de uso.