En este momento estás viendo Análisis: ¿Qué tipo de análisis apoya la detección de “memory leaks” durante la ejecución del software?

Análisis: ¿Qué tipo de análisis apoya la detección de “memory leaks” durante la ejecución del software?

  • Autor de la entrada:
  • Categoría de la entrada:Agile / Bugs / ISTQB

En relación con la unidad 6 – Herramientas de Prueba, el planteo de la pregunta es la siguiente:

¿Qué tipo apoya la detección de “memory leaks” en ejecución?
a) “Static analysis”
b) “Dynamic analysis”
c) “Code review” tool
d) “Test data generation”

Correcta. La opción (b) “dynamic analysis” (análisis dinámico) es la respuesta adecuada para la detección de “memory leaks” (fugas de memoria), ya que implica observar el comportamiento del software en ejecución y medir el uso de recursos en tiempo de ejecución. En contraste, la “static analysis” (análisis estático) y las revisiones no ejecutan el software. En los exámenes de muestra CTFL v4.0 se refuerza que lo dinámico busca provocar fallos/observar comportamientos durante la ejecución, mientras que lo estático no ejecuta el código; además, ejemplos típicos de defectos no detectables por estático incluyen tiempos de respuesta y uso de memoria “demasiado alto”, ambos intrínsecamente dinámicos.

Recordar los siguientes puntos

  • Las fugas de memoria se manifiestan durante la ejecución, por lo que requieren instrumentación/observación en runtime (propio del “dynamic analysis”).
  • El análisis estático (o una “code review” (revisión de código)) puede inferir patrones riesgosos, pero no confirma el comportamiento de uso/acumulación de memoria bajo ejecución real.
  • La “test data generation” (generación de datos de prueba) apoya la preparación de entradas, no la medición de fugas.

Opciones incorrectas (distractores)

  • (a) “Static analysis”: distractor plausible pero incorrecto, porque no ejecuta el software.
  • (c) “Code review” tool: revisión estática; no mide memoria en runtime.
  • (d) “Test data generation”: prepara datos; no detecta fugas.

Clave de aprendizaje

“Dynamic” (dinámico) = ejecución. Todo lo que requiera medición de recursos en runtime (memoria, CPU, tiempo de respuesta) pertenece al ámbito dinámico.

Exam tips

  • Si el fenómeno solo existe en ejecución (p. ej., fugas de memoria, latencia), piense en “dynamic analysis/testing”.
  • Si la actividad no ejecuta el software (requisito, diseño, código leído), es estática.

Fuera del alcance del programa de estudios del ISTQB

Como es mi costumbre, voy un poco más allá e investigué acerca del tema para reforzar el conocimiento y estar más al tanto del alcance que debemos considerar como agile testers, a continuación el resultado.

Criterios prácticos para “fuga confirmada” (recomiendo documentarlo como “Definition of Done”)

  • Prueba A/B: la corrección propuesta elimina la pendiente, manteniendo funcionalidad y rendimiento.
  • Reproducibilidad: la métrica de heap/base de objetos crece en ≥3 ejecuciones consecutivas del escenario sin volver al baseline tras GC/teardown.
  • Evidencia de retención: existe ruta a “GC roots” (JVM) o stack trace de asignación (ASan/heaptrack) que justifica la retención.
  • Ruido acotado: excluir cachés legítimos (con política de expiración), warm-up del runtime y telemetría diferida.
  • Escala temporal: la pendiente de crecimiento es positiva y sostenida bajo carga representativa (p. ej., 15–30 min o N iteraciones).

Plantilla breve de plan de prueba dinámico

  1. Objetivo: detectar y eliminar “memory leaks” y retenciones indebidas mediante “dynamic analysis”.
  2. Alcance: servicios A/B, apps móviles, SPA web; escenarios críticos (login, búsqueda, checkout).
  3. Herramientas: seleccionar 1 primaria (rápida/CI) + 1 secundaria (profunda/laboratorio) según la matriz que hayas elaborado.
  4. Datos de prueba: controlados y reusables (p. ej., “test data generation” para volumen estable).
  5. Métricas: tamaño de heap, objetos vivos por tipo, pendientes de crecimiento, tiempo hasta GC.
  6. Criterios de aceptación: aplicar los 5 criterios de fuga confirmada señalados.
  7. Reporte: snapshot inicial/final, diffs, rutas a raíces, PR con fix y evidencia post-fix.

Casos de Prueba enfocados a los escenarios más críticos

5 criterios prácticos de “fuga confirmada” definidos previamente (Reproducibilidad, Evidencia de retención, Ruido acotado, Escala temporal, Prueba A/B). El formato es Gherkin y puede adaptarse a distintos ecosistemas (JVM/.NET/C/C++/Android/Web/Go).

Feature: Confirmación de «memory leaks» mediante «dynamic analysis»
# Objetivo: confirmar fugas de memoria con evidencia técnica suficiente,
# minimizando falsos positivos y documentando trazabilidad para su corrección.

Background:
Given un entorno controlado con «symbolization» activada (p.ej., -g, mapas de símbolos, o PDB)
And datos de prueba deterministas y constantes entre ejecuciones
And monitoreo habilitado (p.ej., JFR/JMC, Valgrind, dotMemory, LeakCanary, memlab, pprof)
And un escenario de uso representativo del negocio (p.ej., búsqueda, checkout, sesión prolongada)
And criterios cuantitativos acordados (umbrales y tiempos) documentados en el plan de prueba

@critical @criterion1 @runtime @dynamic
Scenario: (C1) Reproducibilidad — crecimiento consistente del heap sin retorno al baseline
Given tres ejecuciones consecutivas del mismo escenario funcional bajo idénticas condiciones
And una captura de snapshot de memoria «baseline» antes de cada ejecución
When ejecuto el escenario completo y fuerzo recolección de basura/teardown controlado al final
And tomo un snapshot «post-ejecución» en cada una de las tres corridas
Then el tamaño de heap u objetos vivos de tipo sospechoso crece en las 3 corridas
And el valor «post-ejecución» no retorna al baseline tras la GC/teardown en ninguna corrida
And el crecimiento relativo supera el umbral acordado (p.ej., ≥10% por corrida o ≥N KB)
# Alineación con criterio 1: Reproducibilidad.

@critical @criterion2 @runtime @dynamic
Scenario: (C2) Evidencia de retención — ruta a «GC roots» o traza de asignación
Given un snapshot comparativo que muestra crecimiento de una o más clases/tipos específicos
And herramientas con soporte de «retention paths» o «allocation stacks»
When inspecciono los objetos vivos con mayor contribución al crecimiento
And obtengo rutas a «GC roots» (JVM/.NET) o «stack traces» de asignación (ASan/heaptrack)
Then identifico al menos una ruta de retención directa o cadena de referencias no liberadas
And documento el punto de asignación y el owner lógico (módulo/servicio/componente)
And registro la hipótesis causal (p.ej., listener no removido, cierre de stream ausente)
# Alineación con criterio 2: Evidencia de retención.

@critical @criterion3 @runtime @dynamic
Scenario: (C3) Ruido acotado — descarte de cachés legítimos y calentamiento del runtime
Given un plan para neutralizar fuentes de ruido (warm-up, cachés aplicacionales, telemetría diferida)
And políticas de caché conocidas (TTL/expiración) documentadas
When repito el escenario con cachés deshabilitadas o con TTL muy corto
And ignoro la primera fase de warm-up definida (p.ej., X minutos o Y iteraciones)
And vuelvo a tomar snapshots comparables tras el período de estabilización
Then el crecimiento anómalo de heap persiste por encima del umbral acordado
And descarto cachés legítimos como explicación (no hay correlación con claves/TTL/estrategias)
# Alineación con criterio 3: Ruido acotado.

@critical @criterion4 @runtime @dynamic
Scenario: (C4) Escala temporal — pendiente positiva sostenida bajo carga representativa
Given una sesión de carga sostenida y realista (p.ej., 30 minutos o N iteraciones del flujo crítico)
And medición periódica del heap/objetos vivos (p.ej., cada 60 segundos o cada M iteraciones)
When ejecuto el escenario con el perfil de usuarios/eventos acordado
And registro la serie temporal de la métrica de memoria
Then la pendiente de crecimiento es positiva y estadísticamente significativa durante el intervalo
And no hay retorno al baseline tras GC periódicas
And el área bajo curva o el delta acumulado superan el umbral pactado (p.ej., ≥X MB en 30 min)
# Alineación con criterio 4: Escala temporal.

@critical @criterion5 @runtime @dynamic
Scenario: (C5) Prueba A/B — eliminación de la pendiente tras el fix sin degradar funcionalidad/rendimiento
Given un cambio de código que implementa la hipótesis de corrección (p.ej., cerrar recursos, desubscribir listeners)
And un conjunto de pruebas funcionales y de rendimiento para no-regresión
When repito el mismo escenario y la misma ventana temporal que en C4 con la versión corregida
And tomo snapshots comparables y recalculo la pendiente
Then la pendiente de crecimiento desaparece o cae por debajo del umbral de significancia
And las pruebas funcionales pasan sin regresiones
And las métricas de rendimiento se mantienen dentro de tolerancias (p.ej., ±5%)
And adjunto evidencia: diffs de snapshots, rutas a raíces antes/después y gráfica de series
# Alineación con criterio 5: Prueba A/B (validación post-fix).


Explicación de ciertas nomenclaturas y términos

La búsqueda de los mismos la llevé a cabo para poder entender el alcance del tema central de la pregunta «memory leaks» y darme cuenta hasta dónde puede llegar y todo el camino que hay que recorrer para lograr un buen desarrollo de software con la calidad esperada. Queda mucho camino por andar, pero lo importante es que «vamos andando». 😎

Servicios A/B (A/B services)
Es una forma de experimentación en producción donde se ejecutan dos versiones del mismo servicio (A y B) en paralelo y se divide el tráfico de usuarios entre ambas (por porcentaje, reglas o segmentos). El objetivo es comparar resultados con datos reales (p. ej., conversión, errores, latencia) para decidir si la versión B supera a la A. Suele apoyarse en “feature flags” (banderas de características) y enrutamiento a nivel de API gateway, service mesh o load balancer. No es un despliegue “blue-green” ni un “canary” (que se enfocan más en reducir riesgo de release); aquí el foco es medir impacto y tomar decisiones basadas en evidencia.

Para profundizar (2 referencias):

  1. Martin Fowler — Feature Toggles (incluye uso para A/B testing). [ver Feature Toggles (aka Feature Flags)]
  2. AWS — Blue/green, canary y A/B con Application Load Balancer (tráfico ponderado).

“SPA web” (single-page application)
Es una aplicación web que carga un único documento HTML y luego actualiza su contenido dinámicamente mediante JavaScript (por ejemplo, con fetch) sin recargar toda la página. El objetivo es ofrecer transiciones más rápidas y una experiencia similar a una app nativa; a cambio, suele requerir enrutamiento del lado del cliente y cuidado con temas como historial del navegador y SEO.

Para profundizar:

  • MDN — Glosario: ¿Qué es una SPA y cómo actualiza el documento con JavaScript? [ver SPA (Single-page application)]
  • MDN — History API: base para manejar navegación/URL en SPAs sin recargar la página. [ver History API]

“Tamaño de heap” (heap size)
Es la cantidad de memoria que una aplicación tiene reservada para crear y mantener objetos en tiempo de ejecución. En entornos con recolector de basura (p. ej., JVM o .NET), el heap es la región administrada donde viven los objetos hasta que dejan de usarse y el GC libera su espacio. Vigilar el tamaño del heap (y su crecimiento) ayuda a detectar fugas de memoria y retenciones indebidas durante pruebas dinámicas.

Para profundizar:

  • Oracle (Java): qué es el Java heap, cómo crece/encoge y cómo interviene el GC. [ver On-Heap and Off-Heap Memory]
  • Microsoft Learn (.NET): fundamentos del managed heap y la recolección de basura.

“Path to GC roots (JVM)” (ruta a “GC roots” en la JVM)
Es la cadena de referencias que conecta un objeto del heap con algún “GC root” (por ejemplo: hilos activos, referencias estáticas, objetos internos de la JVM o referencias JNI). Si existe una ruta, el objeto se considera alcanzable (vivo) y no será recolectado; si no existe, el objeto es candidato a recolección. Las herramientas de perfilado/inspección de heap muestran estas rutas para ayudarte a identificar quién está reteniendo un objeto y confirmar fugas de memoria (p. ej., un listener no liberado o una colección que crece sin límite).

Para profundizar:


“Stack traces” (trazas de pila)
Es el listado ordenado de llamadas de funciones/métodos que estaban activas justo cuando ocurrió un error o cuando se solicita la traza. Permite seguir el “camino” del código hasta el punto del fallo para diagnosticar rápidamente dónde pasó y qué llamadas llevaron a ello. En términos simples: es una “ruta de migas” para depurar.

Para profundizar:

  • Wikipedia — definición general y usos de stack trace. [ver Stack trace]
  • Microsoft Learn (.NET) — qué muestra Exception.StackTrace y cómo leerla.

Puedes seguir también mis publicaciones por LinkedIn.

Gus Terrera

Apasionado por el agile testing y la ia.