Windows Server High CPU / Memory / Disk — Performance Degradation Diagnosis
A Windows Server becomes sluggish, applications time out, or users report slowness. Systematic diagnosis using Resource Monitor, PerfMon counters, and process-level tools identifies whether the bottleneck is CPU saturation, memory pressure, kernel pool exhaustion, or I/O queue depth.
Indicators
- Task Manager shows sustained CPU > 85 % with no obvious single-process cause
- Available memory consistently below 500 MB — excessive paging to disk
- Disk latency > 20 ms sustained — PhysicalDisk\Avg. Disk sec/Transfer
- Users report application slowness or timeouts on server-hosted workloads
- Event ID 2004 (Resource Exhaustion) or 2019/2020 (Non-paged/paged pool exhaustion) in System log
- Disk activity light continuously active with no explicit I/O workload — indicative of excessive paging/thrashing
- Page file / swap usage extremely high (swap used > 50–100% of physical RAM)
- High hard page faults/sec visible in Resource Monitor Memory tab or perfmon
- Linux: OOM killer messages in dmesg or /var/log/syslog — 'Out of memory: Kill process <PID>'
- Applications crash with out-of-memory errors or fail to launch new processes
Likely causes
- Runaway process or service consuming CPU — antivirus full scan, Windows Update, or runaway service
- Memory leak in a service or driver — kernel non-paged pool growing unbounded
- Disk I/O bottleneck — undersized storage, RAID degraded, or SAN path latency
- Insufficient RAM for workload — server hosting more VMs or users than originally sized for
- Hyper-V Dynamic Memory balloon driver stealing guest RAM on over-committed host
- Antivirus real-time scanning directory with high file churn (database, spool)
- Page file / swap too small, disabled, or on a slow disk — exacerbates paging latency under pressure
- Misconfigured application memory limits (e.g. JVM -Xmx too large, SQL Server max server memory unconfigured)
- Memory-mapped files or large caches not released under pressure
- Runaway process entering an infinite allocation loop due to a bug or malformed input
Diagnostic steps
-
Open Resource Monitor (resmon.exe) — review CPU, Memory, Disk and Network tabs to identify the top consuming process and resourceProvides a real-time multi-resource overview to quickly identify which subsystem is saturated and which process is responsible.
-
Open PerfMon and add counters: \Processor(_Total)\% Processor Time, \Memory\Available MBytes, \PhysicalDisk(_Total)\Avg. Disk sec/Transfer, \PhysicalDisk(_Total)\Avg. Disk Queue LengthQuantifies key resource metrics over time to distinguish sustained saturation from transient spikes.
-
Identify top memory consumers: Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object Name, Id, @{N='MB';E={[math]::Round($_.WorkingSet64/1MB,1)}} | Select-Object -First 15Surfaces the top 15 memory-consuming processes on Windows for targeted investigation.
-
Check kernel pool leaks (Event ID 2019/2020): run poolmon.exe (from Windows SDK) — sort by NonP column; identify the tag consuming most non-paged pool and cross-reference against driver listPinpoints kernel-mode pool leaks to a specific driver tag when user-mode process analysis does not fully account for memory consumption.
-
Check disk health: Get-PhysicalDisk | Select FriendlyName, HealthStatus, OperationalStatus; run CrystalDiskInfo for S.M.A.R.T data on physical hardwareIdentifies degraded or failing disks that could be contributing to elevated I/O latency.
-
Capture a 30-minute PerfMon data collector set baseline and export to BLG file for trend analysis; use Windows Admin Center Performance History if availableCreates a historical record for trend analysis and comparison against established baselines.
-
Linux — check overall RAM and swap: free -h && vmstat 2 10 (observe si/so columns for swap-in/swap-out activity over 10 intervals); check OOM events: dmesg | grep -i 'oom\|out of memory\|killed process'Quantifies paging activity on Linux and confirms whether the kernel OOM killer has already terminated processes, establishing severity.
-
Linux — list top memory consumers: ps aux --sort=-%mem | head -20; use sar -r 2 30 for historical memory utilisation if sysstat is installed.Surfaces the specific processes responsible for RAM exhaustion on Linux so remediation can be targeted.
-
Check virtual memory / page file configuration. Windows: System Properties > Advanced > Performance Settings > Advanced > Virtual Memory — note current size and whether system-managed. Linux: swapon --show && cat /proc/sys/vm/swappinessConfirms whether the page file or swap is adequately sized and whether swappiness tuning is contributing to aggressive paging under moderate memory pressure.
-
Use RAMMap (Sysinternals) on Windows to get a detailed physical memory breakdown by type (Active, Standby, Modified, Free) and identify large non-process consumers such as driver-locked pages or mapped files.Reveals memory consumers invisible to Task Manager or poolmon, particularly large standby lists or mapped-file caches consuming physical RAM.
Resolution path
- Identify bottleneck resource (CPU / RAM / disk / network) using resmon and PerfMon
- Kill or restart runaway service; exclude high-churn directories from AV scanning
- For memory leaks: identify pool tag with poolmon, update or roll back driver
- For disk I/O: check RAID health, expand storage, or move heavy workloads to dedicated LUN
- For SQL Server: cap memory via SSMS — EXEC sp_configure 'max server memory (MB)', <value>; RECONFIGURE; to prevent it consuming all available RAM
- Temporarily expand page file (Windows: Initial 1.5× RAM, Maximum 3× RAM via System Properties > Advanced > Performance > Virtual Memory) or add a Linux swap file (fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile) to restore short-term responsiveness
- Tune Linux vm.swappiness to reduce premature paging: echo 'vm.swappiness=10' >> /etc/sysctl.conf && sysctl -p
- Configure automatic service restart as stop-gap for known memory-leaking services: Windows — sc.exe failure <ServiceName> reset= 86400 actions= restart/60000/restart/60000/restart/60000; Linux — systemd RestartSec + Restart=on-failure in the unit file
- Long-term: increase physical RAM or migrate VMs/services to additional hosts to bring peak utilisation below 80% of physical RAM
Prevention
- Establish PerfMon baselines at deployment — capture 24-hour counter log at normal load
- Configure Windows Admin Center alerts or SCOM performance thresholds
- Exclude database data files, spool directories and NTDS from AV real-time scanning
- Right-size RAM before deployment: 4 GB minimum + workload RAM per Microsoft guidance
- Set explicit memory limits on all major applications: SQL Server 'max server memory', JVM -Xmx, container --memory / resources.limits.memory in Kubernetes — prevent any single process exhausting system RAM
- Place the page file on the fastest available storage (NVMe/SSD); do not disable it even on high-RAM systems — Windows requires it for complete crash dumps
- Implement automated service restart schedules for known memory-leaking services (Windows Task Scheduler or Linux systemd timers) as a stop-gap while vendor patches are sought
- Track memory utilisation trends over time; plan RAM upgrades or workload redistribution before utilisation consistently exceeds 70–75% of physical RAM under normal load
- Test all application updates in a staging environment using heap profiling tools (JProfiler, Valgrind, Windows Memory Diagnostic) to detect memory regressions before production deployment
Tools
- resmon.exe — Resource Monitor
- perfmon.exe — Performance Monitor
- poolmon.exe (Windows SDK) — kernel pool leak detection
- Process Explorer (Sysinternals) — per-thread CPU and handle counts
- Windows Admin Center — Performance History
- CrystalDiskInfo — disk S.M.A.R.T health
- Get-Process (PowerShell)
- RAMMap (Sysinternals) — detailed physical memory breakdown by type and process
- htop / top — Linux interactive process and memory monitoring
- free -h — Linux summary RAM and swap usage
- vmstat — Linux virtual memory statistics including swap I/O rates
- ps aux --sort=-%mem — Linux processes sorted by memory usage
- dmesg — Linux kernel OOM killer log entries
- sar — Linux historical memory utilisation reporting
- swapon / fallocate — Linux swap file creation and management