GC frecuente en Windows Server 2016: cómo controlar la recolección de basura sin interrumpir tu API .NET

Cuando la recolección de basura (GC) ocurre con demasiada frecuencia en un entorno de producción, los síntomas son claros: peticiones más lentas, picos de CPU, “micro‑cortes” que los usuarios perciben como fallas intermitentes y administradores que terminan reiniciando la aplicación para «liberar» memoria. En Windows Server 2016 estas pausas pueden provenir tanto del runtime de .NET como de los servicios de deduplicación de disco. A continuación se describe, paso a paso, cómo diagnosticar cada origen, mitigar la interrupción y conservar la disponibilidad de la API sin recurrir a reinicios improvisados.

Índice

Comprender las pausas de GC en Windows Server 2016

GC no es un único mecanismo: el Common Language Runtime (CLR) de .NET ejecuta colecciones generacionales que liberan objetos del montón administrado, mientras que el servicio ddpsvc de Data Deduplication ejecuta su propia limpieza de bloques duplicados. En servidores donde IIS, deduplicación y copias sombra comparten volúmenes, ambos tipos de GC pueden solaparse y amplificar la latencia.

¿Por qué las pausas se sienten como “mini‑cortes”?

  • GC del CLR detiene todos los hilos administrados con un stop‑the‑world cuando realiza colecciones completas (Generación 2 y Large Object Heap). Si la memoria supera cierto umbral, las pausas se hacen más largas y notables.
  • GC de deduplicación (Deep Garbage Collection) lee y reescribe grandes volúmenes de datos. Cuando coincide con picos de tráfico web, compite por CPU, memoria y discos, retrasando las respuestas de IIS.

Diagnóstico: aislar quién colecciona y cuándo

Monitoreo con PerfMon

Abra Performance Monitor y agregue:

  • .NET CLR Memory > % Time in GC
  • .NET CLR Memory > Gen 0, Gen 1, Gen 2 Collections
  • LogicalDisk > Avg. Disk sec/Transfer
  • Dedup Data Reduction > Garbage Collection I/O/sec

Correlacione el momento de cada pico con los logs de IIS y los Event IDs 201, 260 o 264 de deduplicación. Si ve picos mensuales o semanales fuera de horario laboral, es probable que el origen sea la Deep GC de deduplicación.

Revisión de copias sombra

Use vssadmin list shadows para contar instantáneas anteriores. Un volumen con cientos de copias y deduplicación habilitada incrementa mucho la carga de GC de dedup.

Mitigación cuando el origen es Data Deduplication

Separar el área diferencial de VSS

Deduplicación rastrea cambios en el mismo volumen donde IIS lee y escribe logs. Si mueve el almacenamiento diferencial (shadow copies) a un disco más rápido o dedicado, los flushes de VSS no impactarán la cola de disco principal.

Deshabilitar la Deep GC automática

Ajuste el intervalo a 0xFFFFFFFF para evitar colecciones profundas no programadas:

; Servidor independiente
[HKLM\System\CurrentControlSet\Services\ddpsvc\Settings]
"DeepGCInterval"=dword:FFFFFFFF

; Nodo de clúster
\[HKLM\Cluster\Dedup]
"DeepGCInterval"=dword\:FFFFFFFF

Con esto solo se ejecutará la GC «normal», mucho menos intrusiva.

Ejecución manual y fuera de horario

Cuando necesite liberar bloques viejos, programe una tarea nocturna:

Start-DedupJob D: -Type GarbageCollection -Full

Si el trabajo demora más de lo esperado, la orden Stop-DedupJob permite abortarlo sin reiniciar el servidor.

Mitigación cuando el origen es el CLR

Evitar reciclados basados en memoria bruta

En muchos tutoriales se sugiere «reiniciar el App Pool a los 15 GB». El problema es que justo cuando la memoria toca ese umbral, el IIS descarga el dominio de aplicación, vacía cachés y compila de nuevo, provocando un bache de varios segundos. Si el índice de peticiones es alto, varios reinicios encadenados generan un efecto de cascada. En su lugar:

  • Programe un reciclado fijo durante la ventana de menor tráfico (p. ej., 04:00 a. m.).
  • Monitoree el consumo con counter logs; si excede constantemente el 80 % de la RAM, agregue instancias al pool en lugar de recortar memoria.

Activar Server GC en entornos de producción

Agregue al web.config:

<configuration>
  <runtime>
    <gcServer enabled="true"/>
  </runtime>
</configuration>

Server GC emplea varios hilos especializados por núcleo y ofrece pausas más cortas y consistentes bajo carga concurrente. En hardware con 8 núcleos o más, la ganancia suele ser inmediata.

Establecer límites duros de memoria

Si migra a .NET 6+ sobre Windows Server 2019/2022, puede definir:

  • DOTNET_GCHeapHardLimit (bytes exactos).
  • DOTNET_GCHeapHardLimitPercent (porcentaje del total).

El CLR dejará de asignar antes de quedarse sin memoria física, evitando paginación y pausas largas. En .NET Framework 4.8 se puede usar COMPlus_GCHeapHardLimit como variable de entorno.

Reducir colecciones de Generación 2 y LOH

La clave para no disparar Gen 2 cada pocos segundos es minimizar asignaciones de objetos grandes (>85 KB) y de larga vida. Acciones prácticas:

Patrón de códigoImpactoAlternativa sugerida
Construcción frecuente de StringBuilder.ToString()Crea buffers grandes en LOHUsar Span<char> o escritor de flujo
Serializar JSON en memoriaPicos de 1‑2 MB por peticiónSystem.Text.Json incremental
Objetos estáticos con colecciones crecientesPromueve a Gen 2Cache TTL o ConcurrentDictionary

Estrategias de arquitectura para resiliencia

Escalamiento horizontal antes que reinicio

Añadir un séptimo u octavo nodo al balanceador distribuye el aumento de memoria, de modo que ninguna instancia toca el límite duro durante el día. Al terminar la jornada se puede reciclar cada nodo de forma escalonada sin que todo el clúster quede sin capacidad.

Separar roles de disco

  • Volumen C: SO y archivos temporales.
  • Volumen D: Contenido IIS y logs de aplicación (sin deduplicar).
  • Volumen E: Repositorio deduplicado para backups o medios estáticos.

Así, la Deep GC ya no compite con las operaciones web críticas.

Mantenimiento proactivo y parches

Microsoft publicó varias hotfixes que reducen la duración de Deep GC (KB4036479, KB4457127) y mejoran la estabilidad de GC concurrente en .NET Framework 4.8 (KB5004870). Instalar el latest cumulative update debería ser parte del runbook mensual.

Procedimiento completo recomendado

  1. Grabar 24 h de contadores PerfMon para aislar el origen de la pausa.
  2. Si es Data Deduplication:
    • Actualizar CU más reciente.
    • Separar VSS.
    • Deshabilitar Deep GC y programarla manualmente.
  3. Si es el CLR:
    • Habilitar Server GC.
    • Eliminar reciclados por memoria y planificar uno en ventana baja.
    • Aplicar límites duros o escalar nodos.
    • Optimizar código que dispara LOH.
  4. Documentar cada cambio y medir % Time in GC durante la semana siguiente.

Preguntas frecuentes

¿Deduplicación es compatible con volúmenes que hospedan IIS?

Es técnicamente posible, pero no recomendable para contenido dinámico, logs o bases de datos: el ratio de duplicidad es bajo y las operaciones de GC añaden latencia. Use deduplicación solo en archivos grandes, poco cambiantes (backups, ISOs, repositorios multimedia).

¿Qué hago si necesito deduplicación y alto rendimiento web?

Coloque el directorio inetpub en un volumen sin deduplicar y reserve otro volumen para backups. Así se mantiene la ventaja de ahorrar espacio sin penalizar los picos de tráfico.

¿Puedo utilizar GC Concurrente en lugar de Server GC?

Concurrente (Workstation) reduce pausas totales, pero consume más CPU y no paraleliza al máximo. Para APIs concurridas en servidores multicore, Server GC es la práctica estándar.

Conclusión: disponibilidad sin sacrificios

Las pausas de GC son inevitables, pero su impacto puede controlarse. Desvincular las tareas de deduplicación del pico de uso, ajustar los parámetros del CLR y escalar horizontalmente eliminan la necesidad de reinicios reactivos. Con un monitoreo continuo y mantenimientos programados, la API .NET seguirá disponible y estable, incluso en Windows Server 2016.

Resultado esperado: tras aplicar estos pasos, el porcentaje de tiempo empleado en GC debería caer por debajo del 5 % en horas pico, y los usuarios dejarán de experimentar interrupciones. Las colecciones profundas se ejecutarán de forma manual, supervisada y sin afectar la experiencia en producción.

Índice