┌─────────────────────────────────────────────────┐
│ MÁQUINA DEL OPERADOR (Local PC) │
│ │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ JMeter 3.1 │ │ VisualVM 1.3.9 │ │
│ │ Genera carga │ │ Monitorea app │ │
│ │ HTTP/JSON-RPC │ │ CPU/Memoria/GC │ │
│ └────────┬───────┘ └────────┬─────────┘ │
└───────────┼──────────────────────┼──────────────┘
│ │
│ HTTP (puerto 8080) │ JMX (puerto 9999)
│ │
┌───────────▼──────────────────────▼──────────────┐
│ EC2 DE PRUEBAS (Test Environment) │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Tomcat 7.0.99 + 9 Apps ProCash │ │
│ │ Java 1.7 con JMX habilitado │ │
│ │ GC logging activo │ │
│ └────────────────────────────────────────────┘ │
│ │
│ Resultados guardados localmente: │
│ - /opt/tomcat/logs/gc.log │
│ - Heap dumps (bajo demanda) │
│ - Thread dumps (bajo demanda) │
└─────────────────────────────────────────────────┘
Terminal 1: Terminal 2:
$ visualvm $ jmeter -n -t test.jmx
(conectado a JMX 9999) (genera carga HTTP)
│ │
└────────► Observa ◄────────────┘
│
┌─────▼─────┐
│ Tomcat │
│ Apps │
└───────────┘
Solo se necesitan argumentos JVM en setenv.sh:
# JMX Remote (para VisualVM)
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
# GC Logging (para análisis de memoria)
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/opt/tomcat/logs/gc.log
Esto ya está configurado
~/profiling-results/
├── jmeter-testplans/
│ └── procash-workflow.jmx # Test plan grabado
│
├── jmeter-results/
│ ├── results-20251006.jtl # Datos raw de JMeter
│ └── html-report/ # Dashboard HTML
│ └── index.html # ← Abrir en navegador
│
├── visualvm-snapshots/
│ ├── cpu-profile-20251006.nps # Perfil de CPU
│ ├── heap-dump-20251006.hprof # Análisis de memoria
│ └── thread-dump-20251006.txt # Estado de threads
│
└── gc-logs/
└── gc-20251006.log # Log de GC (copiado de servidor)
Ventajas:
# En el servidor EC2 de pruebas
/opt/profiling-results/
├── jmeter-reports/
│ └── 20251006/index.html
├── heap-dumps/
│ └── heap-20251006.hprof
└── gc-logs/
└── gc.log
Ventajas:
Desventaja: Consume espacio en disco
# Opción 1: SCP (copiar archivos individuales)
scp ec2-user@server:/opt/tomcat/logs/gc.log ~/profiling-results/
# Opción 2: Rsync (sincronizar carpetas completas)
rsync -avz ec2-user@server:/opt/profiling-results/ ~/profiling-results/
# Opción 3: Comprimir y descargar
ssh ec2-user@server "tar czf /tmp/results.tar.gz /opt/profiling-results"
scp ec2-user@server:/tmp/results.tar.gz ~/
tar xzf results.tar.gz
# Reporte JMeter (dashboard HTML)
firefox ~/profiling-results/jmeter-results/html-report/index.html
# VisualVM snapshots
visualvm --openfile ~/profiling-results/visualvm-snapshots/cpu-profile.nps
# Heap dumps
visualvm --openfile ~/profiling-results/heap-dump.hprof
┌──────────────────────────────────────────────────────────┐
│ MÁQUINA LOCAL (Operador) │
│ - JMeter: genera carga │
│ - VisualVM: analiza resultados │
│ - Todos los resultados guardados localmente │
└────────┬──────────────────────────┬──────────────────────┘
│ │
│ │
┌────────▼──────────────┐ ┌────────▼──────────────────────┐
│ EC2 TEST │ │ EC2 PRODUCCIÓN │
│ - Profiling activo │ │ - Solo monitoreo básico │
│ - JMeter load tests │ │ - JMX pasivo │
│ - VisualVM profiling │ │ - GC logging │
│ - Heap dumps │ │ - Sin profiling CPU │
└───────────────────────┘ └───────────────────────────────┘
│ │
│ │
└──────► Compara ◄─────────┘
Resultados
(Test vs Producción)
Navegador ──proxy──► JMeter Recorder ──► test-plan.jmx
JMeter (local) ──HTTP──► Tomcat (test EC2)
VisualVM (local) ──JMX──► Tomcat (test EC2)
Test EC2:
/opt/tomcat/logs/gc.log ──┐
├──► Descargar vía SCP
JMeter (local): │
results.jtl ──────────────┤
│
VisualVM (local): │
cpu-profile.nps ──────────┘
~/profiling-results/ ──► JMeter HTML Report
──► VisualVM Analysis
──► Excel Charts
──► Jupyter Notebook
──► Grafana (opcional)
Método Tiempo Total Self Time
============================================================
ServletLogin.doPost() 45.2% 12.3%
├─ DatabaseService.query() 32.9% 28.1%
│ └─ ResultSet.next() 4.8% 4.8%
└─ SessionManager.create() 0.0% 0.0%
Conclusión: El query de login consume 28% del CPU total
Clase Instancias Tamaño (MB)
====================================================
char[] 1,245,892 487.2
String 892,103 142.7
SessionData 12,500 89.3 ← Posible leak
byte[] 456,789 78.9
Conclusión: 12,500 sesiones acumuladas (memory leak)
| Característica | JMeter + VisualVM | YourKit | JProfiler | AWS CodeGuru |
|---|---|---|---|---|
| Costo | $0 | licencia | licencia | $43/mes |
| Java 1.7 | ✅ Sí | ❌ No | ❌ No | ✅ Sí |
| Instalación | Solo JVM args | Agente | Agente | Agente |
| Overhead | <5% | 10-20% | 10-20% | 5-10% |
| Offline | ✅ Sí | ✅ Sí | ✅ Sí | ❌ No |
| Carga real | ✅ JMeter | ❌ No | ❌ No | ✅ Sí |
Ganador: JMeter + VisualVM por costo cero y compatibilidad total
#!/bin/bash
# profiling-automatico.sh
FECHA=$(date +%Y%m%d_%H%M%S)
RESULTADOS=~/profiling-results/$FECHA
# 1. Ejecutar JMeter
jmeter -n -t workflow.jmx \
-Jusers=20 \
-l $RESULTADOS/results.jtl \
-e -o $RESULTADOS/html-report/
# 2. Descargar logs del servidor
scp ec2-user@test-server:/opt/tomcat/logs/gc.log $RESULTADOS/
# 3. Tomar heap dump (vía JMX)
jmap -dump:live,format=b,file=$RESULTADOS/heap.hprof <PID>
# 4. Generar reporte
echo "Profiling completado: $RESULTADOS"
firefox $RESULTADOS/html-report/index.html
❌ Problema: ServletLogin.doPost() consume 45% del CPU
✅ Solución: Agregar índice en tabla usuarios (columna email)
📊 Impacto: Reduce CPU a 12%, mejora respuesta en 300ms
❌ Problema: 12,500 objetos SessionData retenidos (89 MB)
✅ Solución: Implementar timeout de sesión (30 minutos)
📊 Impacto: Reduce uso de heap en 65%
❌ Problema: GC cada 5 segundos, pausas de 200ms
✅ Solución: Aumentar heap de 1GB a 2GB, ajustar CMSInitiatingOccupancyFraction
📊 Impacto: GC cada 20 segundos, pausas de 50ms
No (recomendado):
.jtl, HTML report).nps, .hprof)Sí, varias opciones:
Flujo de resolución:
getUserData())Depende de: