Sorpresas te llevas en la vida, siempre. A pesar de lo que parezca, hoy no ando sermoneador ni nada por el estilo. Es sólo que no se me ocurre como comenzar este post así que escribo lo primero que se me ocurre [:)]. Total, lo interesante viene ahora.
Viaje de emergencia, aplicación ASP con [OOM], servicio interrumpido.
Resumen: problemas… un poco de entretención para unos meses muy aburridos.
Síntomas
Como mencionaba, tenemos una aplicación ASP que de vez en cuando lanza excepciones por falta de memoria, mas conocidos como error 500 ASP 147. Obviamente con el reinicio del proceso, todo vuelve a la normalidad, pero luego de la calma, llega la tormenta.
Como ya es costumbre, se capturaron algunos dumps de memoria cuando se estaba produciendo el error y se analizaron. Los resultados fueron sorprendentes, los que pasan a ser mostrados ahora.
Lo primero muy interesante es que el dump apenas sobrepasaba los 100 megabytes. Un dump contiene, sin entrar en grandes detalles, los datos privados del proceso y las librerías cargadas entre otras cosas. Si son un poco más de 100 megabytes, ¿cómo es posible que haya falta de memoria?. El administrador de tareas confirmaba que el proceso estaba utilizando algo más de 100 megabytes en working set y un poco menos en memoria privada
¿Entonces?
[windbg] entra a ayudarnos. Revisando el estado de cada heap, nos encontramos con lo que muestra el bloque de más abajo. Hay muchas columnas y muchos datos, pero fijemos la atención en las columnas que hacen mención a la memoria reservada y comprometida.
Recordemos que en un sistema operativo Windows, la memoria puede estar en tres estados: libre, reservada y comprometida. Para que una aplicación la pueda utilizar necesita primero reservarla, y luego comprometerla. Después de usarla, la debe des-comprometer (fea palabra, lo sé) y luego liberar (des-reservar, también es fea).
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast |
En el listado anterior, vemos que hay un par de heaps que han reservado (memoria en estado reservado) más de 400 megabytes, pero que sólo están utilizando (memoria en estado comprometido) un poco más de 100 kilobytes. Entre varios, el heap 04a30000, indicado más arriba en negrilla y con la palabra “<- éste”, es uno de los más grandes.
Veamos el detalle de este heap y sus segmentos, listados a continuación.
Index Address Name Debugging options enabled |
Mmm… mmm…mmm…mmm (esto me recuerda una canción de hace unos años), la mayoría de ellos no tiene más de 4 kilobytes usados para bloques de varios megabytes reservados. Si las matemáticas no te ayudan ahora, 1000 en hexadecimal es equivalente a 4096 en decimal.
Análisis de la situación
Recordemos que el manejo de la memoria lo realiza generalmente el sistema operativo aunque algunas aplicaciones pueden utilizar sus propios manejadores de memoria. Desde código ASP (VBScript) o Visual Basic 6.0, como también desde código manejado NO es posible trabajar a este nivel con la memoria. Lo anterior es un problema en un manejador de memoria.
Si no es ASP, VB. 6.0, ¿qué puede ser? (considerando que no hay componentes desarrollados por el cliente en C o C++)
La respuesta la da [DebugDiag]. Quien creo el heap es “Microsoft Data Access Runtime”, es decir, MDAC. Revisando la versión instalada, comprobamos que es la última con Windows Server 2003 SP2. El camino se pone difícil.
Investigación y resolución
Involucrando a las personas adecuadas, aprendimos que este comportamiento es considerado “esperado” cuando se cumplen las siguientes condiciones:
- Se utilizan recordset del lado del cliente (client-side cursor)
- Se obtienen muchos datos, muchos datos de una tabla
Ok ¿client-side cursor?¿que significa “muchos datos”?
“Cliente” es quien consulta la base de datos, que para este caso es IIS/ASP. En ese caso, los datos se llevan al cliente para ser luego procesados.
Después de investigar en el código, se encontró que una consulta estaba retornando más de 2 millones de registros. Eso es mucho [:)]
Reproducción
Decidido a demostrarlo, procedí a hacer unas pruebas con el siguiente código en mi “servidor.”
Y le agregué a mi tabla algo así como 4 millones de registros.
Después de varias ejecuciones, tanto en paralelo como en serie, los contadores de memoria reservada, comprometida y utilización de procesador mostraron esto:
Se puede ver que la memoria comprometida (verde) llegó como mucho hasta 300 megabytes, pero la memoria reservada (roja) aumentó sin mostrar intención de disminuir, llegando casi hasta 900 megabytes.
¿Cuál es la explicación a que no reutilice la memoria reservada y siga reservando más? Al menos yo no tengo la respuesta.
¿Que sucede cuando llegue a 2 gigabytes? [OOM]
Conclusiones
1.- Nunca desplegar “muchos” registros en una página. Mejor aún, nunca pedir muchos registros a la base de datos.
2.- Utilicen server-side cursors. Hagan la prueba con el mismo código y comparen los resultados. [;)]
Saludos,
Patrick
Es impresionante este blog de alto contenido técnico, el 90% de las cosas no las entiendo pero aún asi me parece un trabajo bastante interesante, al autor del blog solo comentarle cual fue su formación técnica para tener los conocimientos q tiene sobre el analisis de aplicaciones? Enhorabuena por el blog!!
Hola,
gracias por tus palabras. Si tuviese que definir cual ha sido uno de los mejores libros que te pueden ayudar a realizar este trabajo, elegiría “Windows Internals” de Mark Russinovich y David Solomon.
Entender el funcionamiento de Windows es la base para el resto. IIS, SQL, ASP, ASPNET y otros productos usan las funcionalidades que el sistema operativo expone.
Para este caso en especial, está involucrado el memory manager de windows, y ojo que no significa que funcione mal, pero entendiendo como funciona la memoria en windows, sabes adonde apuntar cuando estás buscando los soluciones para las aplicaciones con problemas.
Hay otros libros muy útiles también, pero lo mas importante es entender “el ambiente” (el sistema operativo) donde corren las aplicaciones.
Saludos
Patrick
Supongo que sera fundamental como dices y que te habrán hecho falta conocimientos muy sólidos de sistemas operativos para poder ver como funciona todo el tinglado.
Como sugerencia te animo a que en alguna ocasión y ya que esta tan de moda el J2EE y Java escribas algun post con algún análisis sobre rendimiento y monitoreo de las aplicaciones en la JVM.
Un saludo desde España y felicitaciones de nuevo por el blog, francamente interesante.
k8qXw1 frlcgstevxzv, [url=http://hmughedexogu.com/]hmughedexogu[/url], [link=http://qtjydoqhtbif.com/]qtjydoqhtbif[/link], http://augyxjweajqv.com/
I’m getting a new computer but don’t want to lose my Firefox bookmarks. Is there an easy way to save a record of all the URLs in my Bookmarks and then quickly upload them to Firefox on my new computer?.