Tipos de Recolección de Basura en .Net Core
1 de septiembre de 2019 • 5 min de lectura

La gestión de memoria en los lenguajes modernos a menudo es una consideración secundaria. Para todos los efectos, escribimos software sin pensar apenas en la memoria. Esto nos sirve bien, pero siempre hay excepciones…
En California, existen extensos requisitos de informes financieros para las Agencias de Educación Local (LEA, por sus siglas en inglés). Una LEA puede ser un condado, un distrito, una escuela charter o una sola escuela. La mayoría de las LEA crean sus propios informes financieros que generalmente se centran en Excel, no es sorpresa que cada informe sea diferente. Para resolver este problema, la Junta de Educación de California encargó software para generar informes financieros.
Fui parte del equipo de desarrollo.
Mi primera parada fueron los registros de pruebas. Los registros de Ed-Pro apuntaban a un alto uso de memoria, ¿quizás había una fuga de memoria? Un ingeniero observó que los cálculos de Ed-Pro usaban una gran cantidad de memoria de corta duración. Si la memoria no se limpiaba rápidamente, podría parecer una fuga de memoria.
Ed-Pro está construido sobre .Net Core, el framework multiplataforma de Microsoft. En .Net Core, la memoria se divide en tres etiquetas: Corta duración (Gen0), duración media (Gen1) y larga duración (Gen2). Gen0 es para datos de corta duración que rápidamente salen del alcance, Gen1 es para memoria de duración media que permanece un poco más tiempo, también eventualmente sale del alcance y Gen2 es memoria de larga duración que puede vivir durante toda la vida de la aplicación. La memoria Gen0 se reclama constantemente, Gen1 se reclama con menos frecuencia que Gen0, y Gen2 se reclama aún menos frecuentemente que Gen1.
La única manera segura de entender el uso de memoria de Ed-Pro era perfilarlo. A continuación se muestra una captura de pantalla usando dotMemory de JetBrains.
Como se sospechaba, encontramos grandes cantidades de memoria Gen0 (el azul), tanto que parecía que la Recolección de Basura no podía mantenerse al día. Una estrategia para compensar una gran cantidad de memoria causó que la Recolección de Basura oscilara entre aumentar el espacio de memoria (agregando más memoria para el uso de la aplicación) y limpiarla. Durante los ciclos de limpieza, la aplicación no responde.
Al principio, estábamos perplejos, ¿no es el propósito del GC mantener la memoria ordenada? Dos artículos fueron fundamentales en nuestra comprensión de cómo funciona la Recolección de Basura en .Net: el artículo de Mark Vincze Troubleshooting high memory usage with ASP.Net Core on Kubernetes y Fundamentals of Garbage Collection de Microsoft. Ambos son excelentes lecturas y trajeron claridad al uso de memoria en Ed-Pro.
Aquí hay un resumen de lo que aprendimos: hay dos tipos de Recolección de Basura en .Net: Recolección de Basura de Servidor y Recolección de Basura de Estación de Trabajo.
La Recolección de Basura de Servidor hace un par de suposiciones: Primero, que hay amplia memoria disponible y segundo, que los procesadores son multinúcleo y son rápidos. Ambas pueden ser ciertas, pero vivimos en un mundo de máquinas virtuales y Docker donde es más probable que ambas suposiciones sean falsas.
La Recolección de Basura de Servidor permite que la memoria se acumule, en algún punto, hace una de dos cosas: o aumenta el espacio de memoria permitiendo que la memoria crezca o libera memoria huérfana. Cuando elige liberar memoria, la Recolección de Basura inicia el proceso en un hilo de alta prioridad. El hilo de alta prioridad tiene mayor prioridad que la aplicación; si la máquina es rápida, la limpieza no debería notarse. Sin embargo, si no lo es, causará que la aplicación se detenga hasta que se complete la limpieza.
La Recolección de Basura de Estación de Trabajo opera de manera diferente. Funciona continuamente reclamando memoria en un hilo con la misma prioridad que la aplicación. Esto significa que también está compitiendo por recursos con la aplicación, lo que puede causar lentitud en la aplicación. La ventaja es que el uso de memoria de la aplicación puede mantenerse bastante bajo, principalmente cuando usa grandes cantidades de Gen0.
Por defecto, si .Net Core detecta un servidor, ejecuta el tipo de Recolección de Basura de Servidor, que fue el caso con nuestra aplicación. Para ejecutar el tipo de Recolección de Basura de Estación de Trabajo, agrega el siguiente fragmento a tu archivo de proyecto:
<PropertyGroup>
<ServerGarbageCollection>false</ServerGarbageCollection>
</PropertyGroup>
Hicimos este cambio de configuración a Ed-Pro, usando dotMemory, perfilamos la memoria de Ed-Pro con la Recolección de Basura de Estación de Trabajo habilitada y cargamos las mismas pantallas que en la prueba anterior. Aquí están los resultados:
El uso de memoria se reduce significativamente. Las asignaciones Gen0 son virtualmente inexistentes. Más allá de las diferencias en el gráfico, el uso de memoria de la Recolección de Basura de Servidor alcanzó 1 gig mientras que la Recolección de Basura de Estación de Trabajo alcanzó aproximadamente 200 megas.
Cada aplicación es diferente. Nuestra aplicación usaba una tonelada de datos temporales y por lo tanto usa una tonelada de memoria Gen0. Tu aplicación puede aprovechar memoria de mayor duración como Gen1 o Gen2, en cuyo caso la Recolección de Basura de Servidor tiene mucho sentido. Mi consejo es perfilar tu memoria bajo diferentes condiciones para tener una idea de cómo se usa la memoria y luego decidir qué modo es mejor para tu aplicación.
↑ Volver arribaTambién te puede gustar
- Modificar un Archivo Localmente Sin Actualizar el Repositorio Git Remoto 1 min de lectura
- Centraliza la Integridad de tus Datos 2 min de lectura
- Una Implementación de Búsqueda Binaria 1 min de lectura