Análisis de un problema de desbordamiento de memoria en ASP.NET CORE con dotnet-dump

IntroducciónTras desplegar una nueva versión del sistema en producción, los usuarios reportaron lentitud generalizada. Al inspeccionar el servidor, notamos un consumo inusual de memoria RAM (20-30 GB). Un sevricio específico (gestión de órdenes) consumía 13-18 GB de memoria, que disminuía temporalmente tras reiniciar el proceso pero crecía nuevamente.

Dado que el entorno era una red aislada con contenedores Docker minimalistas (sin herramientas de diagnóstico), implementamos un contenedor auxiliar dedicado a la depuración.

Contenido principal1. Configuración del contenedor de diagnóstico

Creamos una imagen Docker con las herramientas necesarias:

# Base en SDK .NET 5 para compatibilidad
FROM mcr.microsoft.com/dotnet/sdk:5.0

# Instalación de utilidades de diagnóstico
RUN dotnet tool install -g dotnet-dump --version 5.0.220101 \
 && dotnet tool install -g dotnet-stack --version 1.0.130701 \
 && dotnet tool install -g dotnet-counters --version 5.0.251802 \
 && dotnet tool install -g dotnet-trace --version 5.0.251802 \
 && apt-get update \
 && apt-get install -y unzip procps \
 && echo 'export PATH="$PATH:/root/.dotnet/tools"' >> /root/.bashrc

# Configuración del PATH
ENV PATH="${PATH}:/root/.dotnet/tools"

CMD ["/bin/bash"]

Estas herramientas permiten:

  • dotnet-dump: Análisis de volcados de memoria con comandos SOS
  • dotnet-stack: Captura de stacks de hilos
  • dotnet-counters: Monitoreo de métricas de rendimiento
  • dotnet-trace: Registro de trazas sin impacto en el proceso
  1. Integración con el contenedor de apliccaión

Ejecutamos el contenedor con permisos elevados:

docker run -d --name app-svc --privileged --cap-add=SYS_PTRACE \
 -e ASPNETCORE_ENVIRONMENT=Development \
 -e COMPlus_EnableDiagnostics=1 \
 -v /host_tmp:/tmp \
 order-app:5.0

Y conectamos el contenedor de diagnóstico:

docker run -it --rm --cap-add=SYS_PTRACE \
 --pid=container:app-svc --privileged \
 -v /host_tmp:/tmp \
 dotnet-debug:5.0

  1. Análisis de métricas del proceso

Usamos dotnet-counters para obtener datos en tiempo real:

dotnet-counters monitor -p 1

Resultados críticos:

  • GC Heap Size: 11,419 MB (heap muy grande)
  • LOH Size: ~3.34 GB (muchos objetos grandes)
  • Gen2 Size: ~8.8 GB (objetos de larga vida)
  • Working Set: 14,910 MB (uso intensivo de memoria)
  1. Diagnóstico del volcado de memoria

Capturamos el volcado con:

dotnet-dump collect -p 1

Análisis del heap:

dumpheap -stat

Identificamos 290,000 instancias de Order.OperApply.ApplyVoucherDetail sin propósito claro. Usando:

dumpheap -mt 00007f117f67e590
gcroot 00007f0ca9b55818

Descubrimos una cadena de referencias:

  • RabbitMQ.Client.Impl.AsyncConsumerWorkService
  • StockManage.Handler.ProductInOutStockStatDistributedHandler
  • Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry[]
  • Order.OperApply.ApplyVoucher
  1. Resolución del problema

La causa era la serialización de AuditLogInfo en el módulo de auditoría de ABP, que incluía objetos de EF Core con contexto de base de datos y carga perezosa activada. Deshabilitar esta serialización resolvió el problema.

ConclusiónEste caso destaca la importancia de revisar las dependencias transitivas y las operaciones de serialización en sistemas con alto volumen de datos. La combinación de RabbitMQ, EF Core y ABP generó una fuga de memoria significativa.

Etiquetas: ASP.NET Core Docker dotnet-dump memoria fuga de memoria

Publicado el 9-26 10:26