EGEL Plus Ingeniería Computacional study materials
EGEL Plus Ingeniería Computacional study resources

Pausas de 8 segundos: ¿GC o presión de memoria en el hipervisor?

EGEL Plus Ingeniería Computacional · Put your knowledge to work in context.

All case studies

Pausas de 8 segundos: ¿GC o presión de memoria en el hipervisor?

Un servicio de análisis corre en una VM Linux con 16 vCPU y 64 GB de RAM dentro de un hipervisor compartido. La JVM usa -Xmx48g sin límites de contenedor y en el host conviven el proceso de respaldo (lee 40 GB de archivos cada 4 horas) y un agente de monitoreo. Cada cuatro horas se observan pausas de 3 a 8 segundos, con si y so distintos de cero en vmstat, 120 mil fallos de página mayores por segundo y pausas de GC de hasta 1.2 s. vm.swappiness está en el valor por defecto, la memoria transparente huge pages está en modo madvise y el hipervisor reporta 4% de steal time. La ventana de respaldo coincide con el pico de consultas analíticas. El equipo propone subir -Xmx a 56g «para que el GC trabaje menos» y ajustar -XX:MaxGCPauseMillis=200 para cumplir un SLA de 2 s.

1. Con la evidencia disponible, ¿el problema es el recolector de basura o la memoria? Haz el balance de memoria y evalúa la propuesta de subir el heap.

Read the worked answer

Es presión de memoria; el GC es síntoma.

Balance: un heap de 48 GB no es todo el proceso. Metaspace, code cache, stacks de hilos, buffers directos, estructuras nativas y el propio runtime suponen típicamente 15–25% adicional: 48 × 1.2 ≈ 58 GB residentes. A eso se suma la page cache que genera la lectura de 40 GB del respaldo. Sobre 64 GB de RAM, el sistema no puede mantener el conjunto de trabajo y empieza a paginar. La prueba está en la evidencia: si/so distintos de cero y 120 mil fallos de página mayores por segundo. Los fallos mayores implican acceso a disco; ningún ajuste de GC los evita.

Por qué subir a 56g empeora: 56 × 1.2 ≈ 67 GB, por encima de la RAM física. Más paginación, pausas más largas, riesgo de que el OOM killer actúe.

Acción: bajar el heap a ~36–40 GB (o -XX:MaxRAMPercentage=60), fijar límites de memoria del contenedor coherentes con el heap y reservar 20–25% para el sistema y page cache. Separar la ventana de respaldo del pico analítico o limitar su tasa. Medir de nuevo: objetivo si/so en cero y fallos mayores en decenas por segundo, no cientos de miles.

Matiz: las pausas de GC de 1.2 s no explican pausas de 8 s por sí solas; la diferencia es tiempo de paginación dentro de la pausa o fuera de ella. Comparar timestamps de GC con los de vmstat confirma la atribución.

2. ¿Por qué -XX:MaxGCPauseMillis=200 no va a cumplir el SLA? Explica el papel de las huge pages y de NUMA en este caso.

Read the worked answer

MaxGCPauseMillis es un objetivo, no una garantía. En G1 es una pista de esfuerzo: el colector ajusta el tamaño del young gen y el número de regiones por ciclo para acercarse, pero no controla el tiempo de traer páginas desde disco. Si una pausa incluye page-in de memoria swapeada, el colector no puede acortarla. Ajustar el objetivo a 200 ms con memoria insuficiente provoca ciclos más frecuentes y más pequeños, lo que suele aumentar la sobrecarga total y no arregla la cola de latencia. Y el SLA de 2 s se incumple por pausas de 3–8 s, que no son de GC.

Huge pages: con THP en madvise, la JVM solo las usa si se le indica (-XX:+UseTransparentHugePages). Ayudan a reducir fallos de TLB en un heap grande, pero khugepaged puede compactar memoria y añadir latencia. En un host con swapping, reservar huge pages explícitas reduce la fragmentación, pero no sustituye a tener RAM suficiente.

NUMA: si la VM abarca varios sockets, la política de first-touch asigna memoria al nodo del hilo que la toca; sin -XX:+UseNUMA y con 16 vCPU repartidas, parte de los accesos serán remotos y añadirán latencia. Hay que verificar la topología que expone el hipervisor antes de asumir que es un solo nodo.

Orden correcto: primero corregir el dimensionamiento de memoria y la ventana de respaldo; después, si el GC sigue siendo el límite, medir con logs de GC (-Xlog:gc*) y ajustar con datos.

3. El hipervisor reporta 4% de steal time. ¿Cómo confirmas si es un problema real y qué haces al respecto?

Read the worked answer

Qué significa: %steal es el tiempo en que la vCPU estaba lista para ejecutar pero el hipervisor le dio el CPU físico a otra cosa. 4% sostenido en 16 vCPU equivale, en promedio, a 0.64 vCPU perdidas; en picos puede ser mucho peor y coincide con las pausas.

Cómo confirmarlo:

  1. mpstat -P ALL 1 y top para ver la distribución de %steal por vCPU; si se concentra en unas pocas, el problema es de asignación, no de carga global.
  2. En VMware, mirar %RDY (ready time); por encima de 5% indica contención de CPU en el host. En KVM, steal_time del vCPU y el overcommit del host.
  3. Descartar causas propias antes de culpar al hipervisor: %iowait alto por el respaldo produce un patrón parecido pero con si/so y latencia de disco, no con ready time.

Qué hacer:

  • No añadir hilos ni subir la concurrencia: con steal positivo, más hilos solo aumentan la contención y el ready time.
  • Solicitar al proveedor reserva de CPU (CPU reservation) o mover la VM a un host con menos overcommit, con datos de %RDY en la mano.
  • Reducir la presión propia: separar el respaldo del pico, bajar el número de hilos del pool a algo cercano a los núcleos efectivos.

Conclusión: el steal de 4% agrava el cuadro pero no lo explica solo; la causa dominante sigue siendo la memoria. Corregir ambas cosas sin medir después atribuiría la mejora a la intervención equivocada.