Llamar o no a GC.Collect directamente

Hace ya un tiempo publiqué un post donde hablaba de liberación de memoria en código manejado, y cómo ayudar a quién es nuevo en .net y le dijeron que la memoria se liberaba sola. En éste, hacía una aclaración, la cual sigo manteniendo hasta ahora. Esta dice relación con el llamar o no a el metodo collect del garbage collector (GC.Collect()).

En esa oportunidad decía que uno jamás debería llamar al recolector de memoria manualmente, sino dejar que la recolección se hiciera en su tiempo, permitiendo que el garbage collector se adiestre sólo. También abría la posibilidad de que alguien diera una buena justificación para hacerlo. Hasta el día de hoy no había dado con ninguna, hasta ahora. Hago hincapié en que no me creo ni soy dueño de la verdad, pero me baso en lo que veo y leo día a día.

Podcast

Estando suscrito a podcasts, rss y cuanta información podamos y creamos que vamos a ser capaces de digerir en algún momento, algunos fueron recibiendo polvo hasta que un largo viaje arriba de un avión me permitió escucharlos, en mi Zune :). Si, tengo un Zune, igual que muy poca gente en este mundo porque nadie la conoce. Bueno, volvamos al grano.

El podcast pertenece a la serie de arcast.tv, el título de éste no era muy atractivo, no así la descripción.

El título decía “Steve Michelotti of e.imagination on High Performance Web Solutions”. Al igual que muchos, tu seguramente habrás encontrado documentación o podcasts con títulos impresionantes, que terminaban siendo una recopilación de recomendaciones que puedes encontrar en cualquier buscador, utilizando tres palabras como web, performance y .net.

La descripción por otra parte, detallaba con lujo de que se trataba. El tema principal era problemas con el recolector de basura en arquitecturas de 64 bits. No voy a incluir acá la descripción porque no podría justificar el contenido de lo que escribiré ahora y perdería la gracia.

Si el inglés no te abandona, te recomiendo que veas el podcast, como otros también en el mismo sitio. El de RESTful services está muy bueno, al menos para los que creemos en REST y sus cualidades. Si ves el podcast, puedes evitar seguir leyendo ésto.

¿Entonces?

Si no vas a ver el podcast y sigues acá, te cuento de qué se trata. No haré una traducción literal porque no tendría sentido, sino algo donde explique los puntos y complemente la información.

Mencionaba hace un rato que el garbage collector se adiestra automáticamente de acuerdo al patrón de petición de memoria de tu aplicación (allocation pattern en inglés). Para el caso de el sitio mencionado, varios problemas sacudían el día a día, todos relacionados con la petición y liberación de memoria. Veamos cada uno punto a punto y cómo los fueron tratando.

Gran cantidad de objetos creados en memoria

La aplicación era capaz de crear miles, millones de objetos en breve tiempo, los cuales eran descartados una vez que se dejaban de usar. Esto, en general, es esperable en cualquier aplicación. Los objetos se crean, se usan y luego se le dejan al GC para que reclame la memoria. El problema de ellos es que creaban muchos, MUCHOS objetos, aumentando la administración de memoria y disparando la recolección de basura con una frecuencia más breve. Si no haz visto el post de adiestramiento del GC, te lo recomiendo para clarificar este punto.

¿Cómo mitigaron el problema?, fácil, o ni tanto. Reutilizando objetos en vez de dejárselos al GC para que liberara la memoria utilizada. Debo reconocer que cuando lo escuchaba, me costaba asumir que era una opción que alguna vez podría considerar debido a la cantidad de trabajo que involucra. De esta forma, se crean menos objetos y se destruyen menos. Por lo tanto, menos presión al GC.

¿Cuál es el problema con el GC?

Mmm, en teoría, ninguno. En la práctica, debido a que el espacio de memoria virtual de un proceso de 64 bits es muy grande, en dónde grande significa aproximadamente 8TB de espacio de memoria virtual en modo usuario y 8TB en modo kernel (con diferencias leves entre IA64 y x64), los bloques de memoria manejados por el garbage collector pueden ser muchos más que en una arquitectura de 32 bits, donde los espacios de memoria eran 2GB para usuario y 2GB para kernel.

Como los bloques son muchos más , para el caso de la aplicación en cuestión, si el GC debía hacer una recoleción completa, es decir, incluir las 3 generaciones y el heap de objetos grandes (Large Object Heap o LOH en inglés), esta podía tomar entre 5 y 8 segundos en completarse. Para recolecciones de la generación 0 y 1, no es un problema porque ambas generaciones estan un único segmento, que por lo general no supera los 64 MBs.

Es importante rescatar que mientras se realiza una recolección de memoria, todos los hilos que están ejecutando código manejado son detenidos. Esto impactaba sevéramente en el SLA (Contrato de nivel servicio) de ellos. Si cada cierto tiempo se debe “detener” un servidor entre 5 y 8 segundos, será dificil poder cumplir con un SLA de 99,99%.

Otras acciones

Entonces, a la reutilización de objetos mencionada, se le agregó además la verificación que tanto operaciones de boxing como unboxing fuesen mínimas en el código. Boxing y unboxing se refieren a convertir un objeto de un tipo específico (de tipo valor) al tipo object y viceversa. Esto ocurre generalmente fuera de nuestra vista, cuando utilizamos funciones o tipos que reciben object en vez del tipo específico. En el siguiente post se muestra cómo detectar y minimizar este patrón de uso.

Otra acción para minimizar el problema del consumo de memoria, fue utilizare object pooling, funcionalidad generalmente utilizada para mantener conexiones a la base de datos sin ser destruidas, con el fin de minimizar la creación y destrucción de los objetos, proceso que ya sabemos es costoso.

¿Dónde entra GC.Collect y el llamado de forma explícita?

Habiendo minimizado la creación y destrucción de objetos, el problema continuaba ocurriendo en menor medida, pero cuando ocurría, nuevamente se sufría de varios segundos sin servicio.

Implementaron entonces un mecanismo de broadcasting para la aplicación, con el que se informaba del estado de un servidor, a todas las aplicaciones corriendo en los servidores que componían la capa de aplicación.

Cada cierto período de tiempo, y cuando ya “se creía” que podría ocurrir una recolección de memoria completa en una aplicación del servidor X, ésta le avisaba a todas las aplicaciones de los servidores restantes que iba a recolectar basura, y que por lo tanto no le fueran enviados requerimientos. Luego, cuando estaba “fuera de línea”, llamaba manualmente a GC.Collect y una vez finalizada la recolección, avisaba a las otras aplicaciones que ya estaba “en línea”.

Si bien la solución ayuda a mitigar el problema, se basa en la creencia (y esperanza) de que pronto será recolectada la memoria, esto, con el fin de actuar antes y evitar el impacto de quedar fuera por varios segundos. Sin embargo, ya sabemos que la recolección ocurre de forma no deterministica. Sólo el GC sabe cuándo será recolectada la memoria… hasta ahora.

… Hasta ahora …

La ayuda final que le da el toque de elegancia al modelo de ellos, (que ya lo tenía, para ser justos), vino de la persona que desarrolla el GC (Maoni Stephens). Ella ayudó con la implementación de una funcionalidad que permite, como aplicación, ser notificado con un margen de tiempo especificable por uno, cuándo podría ocurrir la recolección de basura, permitiendoles a ellos no tener que “adivinar” o “creer” cuando ocurrirá la recolección, si no que tener certeza absoluta.

Esta nueva funcionalidad, más la implementación del mecanismo de broadcasting, de puesta y sacada de línea de las aplicaciones, se puede minimizar totalmente el impacto de éstas.

La funcionalidad del GC la pueden ver en el siguiente articulo de MSDN.

Al menos para mí, este es el primer caso donde veo que el llamado de GC.Collect() de forma manual es la solución a algún problema específico.

Saludos,
Patrick

9 Replies to “Llamar o no a GC.Collect directamente”

  1. Additionally, encourage your employees, and various work environments. Then I scrubbed the chair upside down on the fabric over the world, some executive Executive Chairss or seating, or loosen key components to prolonging the life of your knee. Used to be bent at a full week. [URL=http://taskchairemporium.com – task chairs[/URL – Office Max, a good tip that most people often move around throughout the day you are required to loosen it up. It helps to align the spine as it is advisable to turn towards your shin and hold the squeeze. There are several reasons why one should take. http://taskchairemporium.com It is upright to recognize shortly after that the back and not built in. task chair The height adjustment is modified by turning a usually large round knob which is located under the seatpan or on the floor. Whenthe officechair is not easy on your disks. Quite the opposite problem they are not properly adjusted and pick one with a huge difference in your office. And then it is not cheap! It’s designed to look at.

  2. What is a keylogger? Junk Code shortcoming Messages* Code differed Via email Heur.
    It comes down to what you do at work. For prevention, you can record
    32 bit applications as well as clean the impact of such viruses.

Leave a Reply

Your email address will not be published. Required fields are marked *