Problema y escenarios aplicables
Una API de Java sensible a la latencia se ejecuta en HotSpot JDK 25 con G1 y un heap fijo de 12 GiB: -Xms12g -Xmx12g. Tras lanzar una funcionalidad de caché, el p99 de las solicitudes sube de 120 milisegundos a 2–4 segundos cada 3–5 minutos. La CPU del host no se satura durante los picos. El monitoreo también muestra que la ocupación de la generación vieja después de la recolección sube de 6.1 GiB a 9.2 GiB a lo largo de dos horas, mientras que la tasa de asignación de la aplicación aumenta de unos 600 MiB/s a 1.4 GiB/s. El equipo califica el incidente como un «problema de GC» y propone aumentar primero el heap a 24 GiB.
Todos los números son supuestos de entrevista. El candidato debe explicar cómo demostrar si los picos de latencia realmente se corresponden con pausas de la JVM; cómo utilizar el registro unificado de GC, Java Flight Recorder (JFR) y diagnósticos acotados del heap para distinguir entre una alta tasa de asignación, crecimiento del conjunto activo (live set), objetos G1 humongous, System.gc() explícito y safepoints no relacionados con GC; y cómo elaborar un plan de remediación y validación reversible. El objetivo no es recitar flags del recolector. Es construir una cadena causal desde el síntoma hasta la evidencia, la hipótesis, el experimento y el SLO.
El material público de entrevistas sobre GC en Java trata los eventos stop-the-world y los tiempos de pausa de la JVM como temas centrales. La guía de HotSpot de Oracle para la solución de problemas de pausas prolongadas cubre directamente la memoria insuficiente, la fragmentación del heap, la actividad del sistema operativo y el GC explícito. Esas fuentes hacen de esta una pregunta representativa de diagnóstico de rendimiento de la JVM. No existe una atribución pública confiable de empresas, por lo que companyName se deja vacío.
Qué evalúa el entrevistador
La primera señal es si el candidato alinea la línea de tiempo antes de realizar ajustes. El p99 de las solicitudes, los registros de GC de la JVM, los eventos de JFR, el estrangulamiento (throttling) de CPU en contenedores, los eventos de disco y las métricas del host necesitan la misma base temporal. Un gráfico de heap en diente de sierra solo demuestra que ocurrió una recolección; no demuestra que un pico específico de solicitudes de tres segundos haya sido causado por GC. A la inversa, G1 realiza un trabajo concurrente sustancial, por lo que un ciclo largo de GC no significa que la aplicación haya estado pausada durante todo el ciclo.
La segunda señal es si el candidato separa una pausa individual excesivamente larga de una tasa total de pausa excesiva. Lo primero requiere analizar el tipo de pausa y la temporización de las fases, los datos activos antes y después, los bytes copiados y los tiempos del sistema operativo. Lo segundo suele rastrear la tasa de asignación y la frecuencia de recolección. La duración promedio de GC por sí sola oculta tanto las pausas extremas poco comunes como una gran cantidad de pausas cortas.
La tercera señal es si la evidencia guía la selección de la remediación. La presión de asignación exige encontrar los puntos calientes de asignación. Una línea base creciente de la generación vieja tras el GC exige demostrar que objetos no deseados siguen siendo alcanzables. El crecimiento en Humongous regions exige rastrear objetos que superan la mitad de una región de G1. Un registro con causa System.gc() exige encontrar a quien lo invocó. «Aumentar el heap, bajar el objetivo de pausa o cambiar a ZGC» no es un diagnóstico que se ajuste a todos los síntomas.
Por último, el entrevistador evalúa la conciencia sobre el riesgo en producción. Las estadísticas de heap de JFR desencadenan recolecciones viejas adicionales. Oracle cataloga jcmd GC.class_histogram y los heap dumps como operaciones de alto impacto, y un heap dump solicita un Full GC por defecto. Una respuesta sólida recopila primero evidencia de bajo overhead, obtiene evidencia pesada en una réplica, fuera de horas pico o en un entorno controlado, y utiliza un canary con la misma carga para demostrar que las pausas más bajas no se obtuvieron a costa de un rendimiento (throughput), CPU o memoria inaceptables.
Preguntas para aclarar antes de responder
- ¿Qué está «bloqueado» exactamente? ¿Es la latencia del manejador del servidor, la latencia de extremo a extremo del cliente, todos los hilos
de Java sin avanzar, o solo algunas solicitudes encoladas? ¿Los picos están aislados en una sola instancia y el balanceador de carga, las dependencias o la red muestran el mismo evento?
- ¿Se pueden correlacionar los registros y las métricas con precisión? Necesitamos el mismo ID de instancia, marca de tiempo UTC, tiempo
de actividad de la JVM y versión de release. Si los registros solo tienen marcas de tiempo a nivel de minutos, mejore la observabilidad antes de sacar conclusiones a partir de dos curvas que simplemente se parecen.
- ¿Qué contiene el registro de G1? Como mínimo, conserve el ID de GC, la causa, el tipo de pausa, el heap antes y después
y la duración. Habilite gc+heap, gc+phases y gc+cpu cuando sea necesario. El RSS del proceso o un porcentaje de heap por sí solos son insuficientes.
- ¿Sigue aumentando la línea base de la generación vieja posterior a la recolección bajo una carga comparable? Una meseta
tras el calentamiento de la caché puede ser el conjunto activo esperado. Un crecimiento continuo con una disminución de los bytes recuperados es más consistente con retención o una fuga de memoria.
- ¿Qué límites aplican al contenedor y al host? Verifique la cuota y el estrangulamiento de CPU, swap, fallos de página,
presión de memoria, E/S de registro bloqueante y vecinos ruidosos (noisy neighbors). Un tiempo de reloj (wall time) de GC prolongado con poco tiempo de CPU puede significar que la JVM fue desprogramada de la CPU o que sus páginas fueron enviadas a swap.
- ¿Qué relaciones de asignación y referencias cambiaron en el release? Inspeccione la capacidad de la caché,
el TTL, los tamaños de clave y valor, los buffers de serialización, el tamaño de lote, la concurrencia y la retención a través de ThreadLocals, listeners, colas o colecciones estáticas.
- ¿Cuáles son los objetivos de latencia y throughput? Defina el p99 y el máximo de pausa, el p99 de solicitudes, el throughput,
la tasa de errores y los límites de CPU y memoria. -XX:MaxGCPauseMillis es una sugerencia de objetivo para G1, no un límite estricto para cada pausa.
- ¿Se puede recopilar evidencia pesada de forma segura? Si solo existe una instancia de producción, agregue
capacidad o desvíe el tráfico primero. No extraiga un volcado de un heap de 12 GiB durante el incidente para luego confundir el Full GC de diagnóstico con la falla original.
Marco de respuesta de 30 segundos
«No cambiaría el heap en primer lugar. Correlacionaría cada pico de solicitudes, por instancia y marca de tiempo, con -Xlog:gc*, eventos jdk.GCPhasePause de JFR, estrangulamiento de CPU, fallos de página y latencia de dependencias para medir cuánto tiempo se detuvo realmente la aplicación. Luego dividiría la evidencia en tres grupos: el tipo y la fase de cada pausa; el porcentaje total de tiempo en pausa por ventana de tiempo; y las tendencias en la tasa de asignación, tasa de promoción, ocupación de la generación vieja después de GC y regiones humongous.
Si la caché eleva la asignación pero la línea base post-recolección es estable, usaría JFR para encontrar puntos calientes de asignación y reducir objetos temporales. Si la generación vieja post-GC sigue subiendo, compararía histogramas de clases y obtendría un heap dump en una réplica controlada para inspeccionar dominadores y rutas de retención. Si los Full GCs están precedidos por fallos de evacuación o muchas regiones humongous, inspeccionaría y dividiría arreglos grandes, buffers y lotes. Si la causa es System.gc(), identificaría a quien lo llama. Cada cambio se probaría en un canary con la misma carga frente al p99 de solicitudes, p99 y máximo de pausas, tasa total de pausa, tasa de asignación, conjunto activo post-recolección, CPU, throughput y errores antes del despliegue».
Análisis detallado paso a paso
Paso 1: Construir una línea de tiempo falsable y demostrar si el GC está involucrado.
Para cada pico de latencia, registre la instancia, la ventana de solicitud, la versión del release y la hora UTC. Correlaciónelo con el registro unificado y los eventos de pausa de JFR por ID de GC. Una configuración de inicio base puede conservar datos de GC y safepoints etiquetados y con marca de tiempo en archivos rotativos:
-Xlog:gc*,safepoint:file=/var/log/app/gc-%p.log:time,uptime,level,tags:filecount=10,filesize=100mEste es un ejemplo de diagnóstico; adapte la ruta, la retención y el presupuesto de disco al entorno. En JFR, céntrese en la duración de jdk.GCPhasePause mientras examina también la carga de CPU, los hilos, la E/S de sockets/archivos y los eventos de asignación. La guía de Oracle señala que, para un recolector concurrente, la duración total del ciclo es menos significativa que el tiempo durante el cual la aplicación estuvo realmente pausada.
Cree tres ramas de resultados. Si los picos coinciden uno a uno con las pausas de la JVM, continúe con el análisis de causa raíz del GC. Si la pausa de GC reportada es corta pero un safepoint es largo, inspeccione la razón del safepoint y el tiempo para alcanzarlo. Si ninguno coincide, investigue el estrangulamiento de CPU, bloqueos (locks), E/S, redes y servicios descendentes. Esto hace que la hipótesis de GC sea falsable en lugar de la explicación predeterminada para toda latencia.
Paso 2: Describir el síntoma de GC con un conjunto de métricas, no con un solo gráfico.
Conserve al menos las siguientes series temporales y compare la misma ventana de carga antes y después del release:
Requests: p50 / p95 / p99 / max, throughput, timeouts, error rate
Pauses: pause p50 / p95 / p99 / max, paused time per minute, pause cause
Heap: young / old usage, old-after-GC, reclaimed bytes, promotion rate
Allocation: bytes/s, top allocation sites by class and thread, inside/outside TLAB
G1: young / mixed / Full counts, evacuation failures, humongous regions
System: process CPU, GC CPU, CPU throttling, RSS, swap, major page faults, disk latencyEl formato used-before → used-after (heap-capacity) muestra lo que recuperó una recolección, pero un solo punto no es una tendencia. Una línea base de la generación vieja post-GC monótonamente creciente con carga comparable sugiere crecimiento en el conjunto activo o presión de promoción. Una línea base estable con recolecciones cada vez más frecuentes apunta más a menudo a una alta asignación. Cuando gc+cpu=info muestra un tiempo real muy superior al tiempo de usuario más sistema, investigue la planificación del SO, cuotas o paginación antes de agregar hilos de GC.
Paso 3: Usar la evidencia para separar cinco caminos de causa raíz.
- Alta tasa de asignación. La generación vieja post-GC es estable, mientras que el recuento de pausas jóvenes y el tiempo en pausa por minuto
aumentan. Los eventos de asignación de JFR apuntan a la construcción de claves de caché, serialización o colecciones temporales. Reduzca la copia, reutilice buffers con un ciclo de vida claro y controle el tamaño de los lotes. No introduzca pools de objetos amplios ni estado compartido sin evidencia.
- Crecimiento del conjunto activo o fuga de memoria. La generación vieja post-GC sigue subiendo y cada recolección recupera menos.
Compare histogramas de clases en múltiples momentos. Obtenga un heap dump únicamente en una réplica fuera de horas pico o en un entorno de reproducción de tráfico, y luego use el tamaño retenido, un árbol de dominadores y las raíces de GC para distinguir una caché legítima de una retención ilimitada. Una caché que se estabiliza al alcanzar la capacidad es un problema de dimensionamiento; los objetos expirados que siguen referenciados indican una fuga.
- Objetos G1 humongous. G1 trata un objeto de al menos la mitad del tamaño de una región como humongous y lo coloca
directamente en regiones viejas contiguas. Si Humongous regions: X → Y se mantiene alto, rastree arreglos grandes de byte[], char[], bloques comprimidos o lotes y reduzca primero el tamaño individual del objeto o lote. Considere un cambio en el tamaño de región como un experimento solo después de medir, ya que también altera el número de regiones y la granularidad de la recolección.
- El marcado concurrente se retrasa o la evacuación falla. Si el registro muestra espacio de destino insuficiente
o regiones que no se pueden evacuar, el peor escenario es un Full GC de todo el heap. Reduzca la asignación o promoción en regiones viejas, proporcione margen suficiente al marcado concurrente y verifique el margen de seguridad entre la capacidad del heap y el conjunto activo. Bajar solo MaxGCPauseMillis puede hacer que cada recolección realice menos trabajo y deje al sistema más cerca del agotamiento.
- GC explícito o un safepoint no relacionado con GC. Si la causa del GC es
System.gc(), use trazas de pila (call stacks), configuración
de dependencias y comandos operativos para encontrar el origen antes de eliminar la llamada, configurar la biblioteca o aislar la tarea. Use DisableExplicitGC solo después de verificar el impacto semántico. Si el safepoint es largo pero la pausa de GC es corta, inspeccione la causa real (como desoptimización, redefinición de clases u otra operación de la VM) en lugar de cambiar recolectores.
Paso 4: Estratificar las herramientas de diagnóstico y controlar el riesgo de observación.
Comience con la capa de menor costo y continuamente disponible: SLIs de la aplicación, registros unificados de GC/safepoints y JFR estándar. Para confirmación en vivo, ejecute jcmd PID JFR.start, JFR.check y JFR.dump en el mismo host como el mismo usuario efectivo. Verifique primero las opciones soportadas por esa JVM con jcmd PID help COMMAND.
Luego pase a la capa de alto costo. Oracle cataloga GC.class_histogram como de alto impacto. GC.heap_dump también es de alto impacto y solicita un Full GC por defecto. Las estadísticas de heap de JFR desencadenan recolecciones viejas adicionales al inicio y al final, por lo que no habilite las estadísticas de heap por defecto durante una investigación de latencia. Desvíe el tráfico o reproduzca el problema en una réplica, confirme la capacidad de disco y el manejo de datos sensibles, y solo entonces obtenga un histograma o volcado. Comparar dos capturas separadas por un intervalo de carga estable explica el crecimiento mejor que una sola lista de los objetos más grandes.
Paso 5: Mantener la causalidad singular al cambiar código, capacidad o parámetros del recolector.
Comience con la nueva caché. ¿Es ilimitada? ¿Su TTL realmente hace expirar las entradas? ¿El peso máximo se mide en bytes en lugar de cantidad de entradas? ¿Cada valor copia un arreglo grande? ¿Las fallas de caché concurrentes construyen el mismo valor de forma independiente? Las posibles correcciones de código incluyen limitar el peso máximo, unificar cargas de la misma clave, serialización por streaming, reducir el tamaño de lote o liberar referencias a objetos temporales más rápido. Cambie una sola variable principal en cada experimento.
Un heap más grande puede aumentar el intervalo entre recolecciones, pero también puede ocultar una retención ilimitada, elevar el riesgo de memoria en el contenedor y aumentar la cantidad de datos activos que eventualmente deben procesarse. Antes de modificar IHOP, el dimensionamiento de la generación joven, el tamaño de región o el objetivo de pausa, identifique la fase registrada o el déficit de recursos que se espera corregir con el flag. Cambiar a un recolector como ZGC es un experimento arquitectónico que requiere una nueva validación de throughput, CPU, margen de heap, contenedor y operaciones. No es el primer comando ante un incidente.
Paso 6: Cerrar el incidente con un canary de carga equivalente y evidencia contrafáctica.
Reproduzca solicitudes con el patrón de producción, tamaños de objetos, tasa de aciertos de caché y concurrencia frente a las versiones base y corregida. El canary debe abarcar varios de los ciclos originales de picos de 3–5 minutos e incluir el arranque en frío de la caché y el estado estable. Defina los umbrales de aceptación antes de la prueba, por ejemplo: p99 de solicitudes por debajo de 200 ms; p99 de pausa de GC por debajo de 100 ms y máximo por debajo de 500 ms; menos del 1% de tiempo en pausa por minuto; que la generación vieja post-GC no siga subiendo tras el calentamiento; y sin regresión en throughput, presupuesto de CPU, OOMs o errores.
Luego pruebe contrafácticos. ¿Revertir la caché restaura tanto la tasa de asignación como los picos? ¿Limitar únicamente el peso de la caché estabiliza la generación vieja post-GC? ¿Reducir solo el tamaño de lote reduce las regiones humongous? Cuando las métricas predichas se mueven juntas, la remediación tiene un vínculo causal con la causa raíz. Despliegue en fases y conserve criterios de reversión automática.
Respuesta de muestra de alta calidad
«Los datos actuales justifican investigar el GC, pero no demuestran que el GC haya causado los picos de solicitud de 2–4 segundos. Primero alinearía los SLIs de solicitudes, -Xlog:gc*,safepoint, los eventos jdk.GCPhasePause de JFR, el estrangulamiento de CPU, los fallos de página y la latencia de dependencias por instancia y hora UTC. Si los picos coinciden con las pausas, distinguiría una pausa larga de muchas pausas cortas acumuladas. Si solo el safepoint es largo, seguiría la causa del safepoint. Si ninguno coincide, saldría de la rama de GC.
El release elevó la asignación de 600 MiB/s a 1.4 GiB/s mientras que la generación vieja post-GC subió de 6.1 GiB a 9.2 GiB. Eso me plantea al menos dos hipótesis: la caché generó una asignación temporal sustancial, y la caché o los objetos relacionados expandieron el conjunto activo. Inspeccionaría las causas de ciclos young, mixed y Full; el heap antes y después; la promoción; los fallos de evacuación; y las regiones humongous. Los eventos de asignación de JFR identifican clases, hilos y puntos de llamada. Los histogramas en múltiples momentos muestran qué clases continúan creciendo. Obtendría un heap dump únicamente en una réplica sin tráfico o en reproducción, ya que es de alto impacto y puede solicitar un Full GC por defecto.
Si la línea base post-recolección es estable pero las pausas young son frecuentes, reduciría la asignación en la construcción de claves de caché, serialización y colecciones temporales. Si la línea base sigue subiendo, usaría el tamaño retenido y las rutas a raíces de GC para distinguir una caché acotada de una fuga. Si arreglos grandes superan la mitad de una región de G1 y el uso de regiones humongous crece, dividiría buffers o lotes. Si la causa es System.gc(), encontraría al invocador. Si el tiempo real excede en gran medida el tiempo de CPU de GC, investigaría cuotas, swap y contención en el host.
No trataría un heap de 24 GiB como la solución. Probaría cada cambio candidato de forma independiente en un canary con la misma carga a lo largo del arranque en frío de la caché y varios ciclos originales de picos. La validación final cubre p99 de solicitudes, p99 y máximo de pausa, tasa total de pausa, tasa de asignación, generación vieja post-GC, regiones humongous, throughput, CPU, RSS, errores y OOMs. Solo entonces incrementaría el tráfico, manteniendo la posibilidad de reversión».
Errores comunes
- **Error: declarar que el GC es la causa tras ver un gráfico de heap en diente de sierra → Por qué falla: el gráfico demuestra
que ocurrió una recolección, no que un pico de solicitudes y una pausa stop-the-world coincidieran en una instancia → Corrección: correlacione solicitudes, registros y JFR con la instancia, el ID de GC y una sola línea de tiempo.**
- **Error: fijarse únicamente en la duración promedio de GC → Por qué falla: el promedio oculta un evento extremo
de tres segundos e ignora el costo total de muchas pausas cortas → Corrección: mida percentiles de pausa, máximo, tiempo en pausa por minuto y causa.**
- **Error: tratar la duración del ciclo de GC como la duración de la pausa de la aplicación → Por qué falla: gran parte del trabajo
de marcado de G1 puede ejecutarse de forma concurrente con la aplicación → Corrección: utilice eventos de pausa reales y SLIs de solicitudes.**
- **Error: aumentar el heap de 12 GiB a 24 GiB durante el incidente → Por qué falla: esto puede ocultar una retención
ilimitada y aumentar el riesgo de memoria sin demostrar que el conjunto activo tiene un límite válido → Corrección: separe la tasa de asignación del crecimiento del conjunto activo post-recolección y luego ejecute un experimento de capacidad reversible.**
- **Error: tomar inmediatamente un heap dump en la única instancia de producción → Por qué falla: el comando
es de alto impacto, puede solicitar un Full GC por defecto, escribe un archivo grande y expone datos sensibles → Corrección: desvíe el tráfico y recopílelo en una réplica o en un entorno de reproducción controlado.**
- **Error: cambiar el tamaño de región de G1 tras notar objetos grandes → Por qué falla: un objeto grande puede no
superar el umbral de media región, y el flag modifica la granularidad de las regiones en todo el heap → Corrección: confirme los objetos humongous con gc+heap y evidencia de asignación, luego compare experimentos.**
- **Error: tratar
-XX:MaxGCPauseMillis=50como un SLA → Por qué falla: es una sugerencia de objetivo y G1 no es
un recolector de tiempo real → Corrección: valide la distribución real de pausas y pondere el balance entre el objetivo, el throughput y el margen de heap.**
- **Error: deshabilitar globalmente el GC explícito en cuanto aparece
System.gc()→ Por qué falla: una dependencia
o flujo de trabajo operativo puede depender de su semántica → Corrección: identifique al invocador y su intención antes de eliminarlo, configurarlo o aislarlo.**
- **Error: verificar solo que el p99 caiga tras cambiar de recolector → Por qué falla: pausas más bajas pueden
implicar mayor uso de CPU, menor throughput o más memoria → Corrección: pruebe latencia, throughput, CPU, RSS, errores y recuperación bajo la misma carga.**
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Cómo se distingue una fuga de memoria del calentamiento normal de la caché?
Examine el conjunto activo tras múltiples recolecciones viejas bajo una carga comparable, no solo la pendiente inicial tras el inicio del proceso. Una caché acotada normal se estabiliza tras alcanzar su peso máximo y una tasa de aciertos estable, y la expulsión (eviction) y la expiración por TTL deberían ser observables. Una fuga deja objetos sin valor de negocio alcanzables desde una raíz de GC, por lo que la línea base sigue subiendo. Use histogramas de clases en distintos momentos para encontrar las clases que crecen, luego inspeccione los dominadores, el tamaño retenido y las rutas de referencia en un heap dump controlado. Incluso si la caché eventualmente se estabiliza, una plataforma de 9.2 GiB en un heap de 12 GiB puede dejar un margen inadecuado para el marcado concurrente y las ráfagas, lo que lo convierte en un problema de diseño de capacidad.
Pregunta de seguimiento 2: ¿Por qué no reducir MaxGCPauseMillis en primer lugar?
Orienta cuánto trabajo intenta realizar G1 por recolección; no es un límite superior estricto. Si el conjunto activo es demasiado grande, el marcado concurrente se retrasa o el contenedor no puede suministrar CPU, un objetivo más bajo puede hacer que cada recolección mixta recupere menos, aumente la frecuencia y acerque el proceso a un fallo de evacuación. Primero formule una hipótesis a partir de los tiempos de fase, el margen de heap y la tasa de asignación o promoción, y luego valide el cambio de un solo flag bajo la misma carga.
Pregunta de seguimiento 3: ¿Qué sugiere un tiempo real prolongado pero un tiempo de usuario y sistema corto en el registro de GC?
Sugiere que los hilos de GC no consumieron CPU durante todo el intervalo de reloj. Las causas posibles incluyen estrangulamiento de CPU del contenedor, contención en el host, swap o fallos de página mayores, pausas de virtualización y E/S de registro. Inspeccione la cuota de cgroup y el tiempo estrangulado, la cola de ejecución (run queue), fallos de página, swap, latencia de disco y eventos en el host compartido. Agregar hilos paralelos de GC puede intensificar la contención; aborde primero la evidencia de suministro de recursos externos.
Pregunta de seguimiento 4: ¿Cuándo consideraría migrar de G1 a ZGC?
Considérelo cuando el servicio tenga un objetivo explícito de baja latencia, la asignación y el crecimiento del conjunto activo estén bajo control, G1 siga sin cumplir el SLO de pausa con el heap y la carga objetivo, y el equipo pueda validar el uso adicional de CPU, margen de heap, compatibilidad con JDK y aspectos operativos. Realice una prueba A/B con tráfico similar al de producción, incluyendo arranque en frío, estado estable, ráfagas, recuperación ante fallos y límites de contenedores. La migración de recolector es una decisión de capacidad y modelo de runtime; no reemplaza la corrección de una fuga de objetos o una caché ilimitada.
Pregunta de seguimiento 5: ¿Cómo demuestra que la corrección es duradera y no solo retrasa el pico?
Ejecute el canary el tiempo suficiente para cubrir varios ciclos de picos originales, el estado estable de la caché y el pico previsto. Compare la pendiente de la generación vieja post-GC, los Full GCs por hora y fallos de evacuación, la tasa de asignación, las regiones humongous, el ratio total de pausas y los SLIs de solicitudes, y pruebe más allá del pico previsto para verificar el margen. Si un heap más grande solo traslada el primer pico de cinco a diez minutos mientras la pendiente de la línea base permanece sin cambios, la remediación ha fallado.