Loading learning content...
When Windows needs more physical RAM than is available, the traditional approach is paging—writing infrequently-used pages to the paging file on disk. But disk I/O is slow, even on modern SSDs, often thousands of times slower than RAM access. Memory compression introduces a middle tier: instead of immediately writing pages to disk, Windows compresses them and keeps them in RAM.
Introduced in Windows 10, memory compression dramatically improves performance for memory-constrained scenarios. A page that would take milliseconds to retrieve from disk can be decompressed from RAM in microseconds. The trade-off: CPU cycles for compression/decompression versus I/O wait time. On modern multi-core processors, this trade-off strongly favors compression.
By the end of this page, you will understand how memory compression fits into the Windows memory hierarchy, the mechanics of the compression store and compression algorithms, how to monitor compression effectiveness, when compression helps versus hurts, and the relationship between compression and the paging file.
Windows memory compression exists as an intermediate tier between active RAM and the paging file. When the Memory Manager decides to trim pages from a process's working set, instead of immediately writing them to disk, it first attempts compression.
The Memory Hierarchy with Compression:
[Active Working Set] → [Modified List] → [Compressed Store] → [Paging File]
RAM (fast) RAM (pending) RAM (compressed) Disk (slow)
Key Components:
The Compression Store:
Compressed pages are stored in a special region managed by the "Memory Compression" process (visible in Task Manager). This store is itself pageable—if memory pressure continues, the compressed store can be paged to disk. But critically, reading back compressed pages from RAM is orders of magnitude faster than reading from disk.
| Memory Location | Typical Latency | Relative Speed |
|---|---|---|
| Active RAM (working set) | ~100 ns | 1× (baseline) |
| Standby List (soft fault) | ~500 ns - 1 μs | 5-10× |
| Compressed Store | ~5-50 μs | 50-500× |
| SSD (NVMe) | ~50-100 μs | 500-1000× |
| SSD (SATA) | ~100-500 μs | 1000-5000× |
| HDD | ~5-15 ms | 50,000-150,000× |
Even with the CPU overhead of decompression, retrieving a page from the compression store is typically 10-100× faster than reading from an SSD, and 1000× faster than reading from an HDD. Modern CPUs can decompress pages faster than the fastest storage can deliver them.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
Windows Memory Compression Flow:════════════════════════════════════════════════════════════════════════════ PAGE EVICTION PATH (Working Set → Compressed Store):┌──────────────────────────────────────────────────────────────────────────┐│ ││ 1. Memory Manager decides to trim pages from process working set ││ └─ Triggered by: memory pressure, aging, priority rebalancing ││ ││ 2. Pages moved to Modified List ││ └─ These are dirty pages awaiting write-back ││ ││ 3. Compression worker threads pick up modified pages ││ └─ Multiple worker threads (scales with CPU cores) ││ └─ Uses XPRESS compression algorithm ││ ││ 4. Compressed data written to Compression Store ││ └─ If compression ratio poor (< ~1.5:1), may skip to page file ││ └─ Original page becomes free for reuse ││ ││ Result: Original 4 KB page now occupies ~1-2 KB in compression store ││ │└──────────────────────────────────────────────────────────────────────────┘ PAGE RETRIEVAL PATH (Compressed Store → Working Set):┌──────────────────────────────────────────────────────────────────────────┐│ ││ 1. Process accesses virtual address whose page is compressed ││ └─ Page table entry indicates "not present" ││ ││ 2. Page fault handler determines page is in compression store ││ └─ Lookup by page frame number (PFN) in store index ││ ││ 3. Decompress page into new physical frame ││ └─ Fast: XPRESS decompression is highly optimized ││ └─ ~5-20 microseconds typical ││ ││ 4. Update page table, resume thread ││ └─ Process continues with page now in working set ││ ││ Result: Page retrieved without any disk I/O ││ │└──────────────────────────────────────────────────────────────────────────┘ COMPRESSION STORE PAGING (when very low on RAM):┌──────────────────────────────────────────────────────────────────────────┐│ ││ If memory pressure continues: ││ ││ 1. Compression store itself allocated from RAM ││ 2. When truly out of RAM, compression store pages go to page file ││ 3. This is normal paging, but with ~2:1 less I/O (compressed data) ││ 4. Retrieval: Disk I/O + Decompression (still faster than HDD for ││ uncompressed, but now involves disk) ││ │└──────────────────────────────────────────────────────────────────────────┘When you open Task Manager, you'll notice a process called Memory Compression (or "System and compressed memory" in older Windows 10 versions). This isn't a traditional process—it's a special container that represents the compression store.
What the Memory Compression "Process" Represents:
Understanding the Memory Compression Metrics:
In Task Manager's Performance tab, click Memory to see compression statistics:
Compression Workers:
The actual compression work is performed by kernel threads that run in the System process. These workers:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
# Get memory compression statistics # Method 1: WMI/CIM$mem = Get-CimInstance Win32_OperatingSystem$cs = Get-CimInstance Win32_ComputerSystem $totalRAM = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2)$freeRAM = [math]::Round($mem.FreePhysicalMemory / 1MB, 0) Write-Host "Memory Overview" -ForegroundColor CyanWrite-Host "════════════════════════════════════════════════════"Write-Host "Total Physical RAM: $totalRAM GB"Write-Host "Free Physical RAM: $freeRAM MB" # Method 2: Performance Counters for compression$compressionCounters = @( 'MemoryStandby Cache Reserve Bytes', 'MemoryStandby Cache Normal Priority Bytes', 'MemoryStandby Cache Core Bytes', 'MemoryModified Page List Bytes') Get-Counter $compressionCounters -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples | ForEach-Object { $name = ($_.Path -split '\')[-1] $valueMB = [math]::Round($_.CookedValue / 1MB, 0) Write-Host "$name`: $valueMB MB" } # Method 3: Check Memory Compression process$compProc = Get - Process - Name "Memory Compression" - ErrorAction SilentlyContinueif($compProc) { $wsMB = [math]:: Round($compProc.WorkingSet64 / 1MB, 0) Write - Host "`nCompression Store Size: $wsMB MB" # Estimate compression ratio(rough) # Actual compressed data ≈ working set of compression process # Original data ≈ Working set × compression ratio(typically ~2) $estimatedOriginal = $wsMB * 2 Write - Host "Estimated original data: ~$estimatedOriginal MB" Write - Host "Estimated space saved: ~$($estimatedOriginal - $wsMB) MB" } else { Write- Host "`nMemory Compression process not visible (may be minimal)"} # Method 4: Detailed via Resource MonitorWrite- Host "`nFor detailed compression view:"Write - Host " Run: resmon.exe"Write - Host " Go to Memory tab"Write - Host " View 'Physical Memory' bar breakdown" # Continuous monitoringWrite - Host "`nPress Ctrl+C to stop monitoring..."while($true) { $proc = Get - Process - Name "Memory Compression" - ErrorAction SilentlyContinue $avail = (Get - Counter 'MemoryAvailable MBytes').CounterSamples[0].CookedValue $compMB = if ($proc) { [math]:: Round($proc.WorkingSet64 / 1MB) } else { 0 } $timestamp = Get - Date - Format 'HH:mm:ss' Write - Host "$timestamp | Available: $([math]::Round($avail))MB | Compressed Store: $compMB MB" Start - Sleep - Seconds 5} A large Memory Compression process isn't necessarily bad—it indicates the system is successfully keeping data in RAM that would otherwise be paged to disk. However, consistently high compression with visible performance impact suggests you should add more RAM or reduce memory-hungry applications.
Windows uses the XPRESS compression algorithm for memory compression, the same algorithm used for NTFS file compression and hibernation files. XPRESS is designed for speed rather than maximum compression ratio.
XPRESS Characteristics:
Compression Effectiveness by Data Type:
| Data Type | Typical Ratio | Notes |
|---|---|---|
| Zero-filled pages | Extreme (100:1+) | Common in newly allocated memory |
| Text/strings | 3:1 to 5:1 | Excellent compression |
| Structured data (JSON, XML) | 2:1 to 4:1 | Good compression |
| Binary data (general) | 1.5:1 to 2:1 | Moderate compression |
| Already compressed (JPEG, ZIP) | 1:1 or worse | May expand slightly |
| Random/encrypted data | 1:1 | Cannot compress |
Performance Characteristics:
Compression Speed:
Decompression Speed:
CPU Impact:
When Compression Doesn't Help:
The system skips compression for pages that don't compress well:
These pages go directly to the modified list for eventual paging to disk.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
# Monitor compression - related performance # Relevant counters for compression analysis$counters = @( 'MemoryPages/sec', # Total paging activity'MemoryPage Reads/sec', # Reads from disk'MemoryPage Writes/sec', # Writes to disk'MemoryAvailable MBytes', # Free memory'MemoryCommitted Bytes', # Commit charge'Processor(_Total)% Processor Time' # CPU usage) Write - Host "Memory & Compression Performance Monitor" - ForegroundColor CyanWrite - Host "═══════════════════════════════════════════════════════════════════"Write - Host "Press Ctrl+C to stop"Write - Host "" $baseline = $null$samples = @() Get - Counter - Counter $counters - SampleInterval 5 - Continuous | ForEach - Object { $sample = $_ $timestamp = Get - Date - Format 'HH:mm:ss' $vals = @{} foreach($cs in $sample.CounterSamples) { $name = ($cs.Path - split '\')[-1] $vals[$name] = $cs.CookedValue } # Get compression store size $compProc = Get - Process - Name "Memory Compression" - ErrorAction SilentlyContinue $compMB = if ($compProc) { [math]:: Round($compProc.WorkingSet64 / 1MB, 0) } else { 0 } # Calculate efficiency metrics $pagesSec = [math]:: Round($vals['Pages/sec'], 0) $diskReads = [math]:: Round($vals['Page Reads/sec'], 0) $diskWrites = [math]:: Round($vals['Page Writes/sec'], 0) $avail = [math]:: Round($vals['Available MBytes'], 0) $cpu = [math]:: Round($vals['% Processor Time'], 1) # Disk activity vs total tells us compression effectiveness $diskPct = if ($pagesSec - gt 0) { [math]:: Round(($diskReads + $diskWrites) / $pagesSec * 100, 1) } else { 0 } # Compression is helping when disk % is low but pages / sec is high $status = if ($pagesSec - lt 50) { "Low activity" } elseif($diskPct - lt 20) { "Compression handling load well" } elseif($diskPct - lt 50) { "Mixed (some disk I/O)" } else { "Heavy disk paging" } Write - Host "$timestamp | Avail: $avail MB | CompStore: $compMB MB | " - NoNewline Write - Host "Pages: $pagesSec/s | Disk: $diskPct% | CPU: $cpu% | " - NoNewline $statusColor = switch ($status) { "Low activity" { "Gray" } "Compression handling load well" { "Green" } "Mixed (some disk I/O)" { "Yellow" } "Heavy disk paging" { "Red" }}Write - Host $status - ForegroundColor $statusColor} # Analysis functionfunction Analyze-CompressionEffectiveness { Write - Host "`nCompression Effectiveness Analysis" - ForegroundColor Green Write - Host "═══════════════════════════════════════════════════════════════════" $compProc = Get - Process - Name "Memory Compression" - ErrorAction SilentlyContinue if ($compProc) { $compMB = [math]:: Round($compProc.WorkingSet64 / 1MB, 0) # Rough estimate: assume 2: 1 average compression $estimatedOriginal = $compMB * 2 $savedMB = $estimatedOriginal - $compMB Write - Host "Compression Store Size: $compMB MB" Write - Host "Estimated uncompressed size: $estimatedOriginal MB" Write - Host "Estimated RAM saved: $savedMB MB" Write - Host "Estimated compression ratio: 2:1 (typical)" $avail = (Get - Counter 'MemoryAvailable MBytes').CounterSamples[0].CookedValue if ($savedMB - gt $avail) { Write - Host "`nCompression is preventing significant paging!" - ForegroundColor Green } else { Write - Host "`nSystem has headroom even with current compression" - ForegroundColor Cyan } } else { Write - Host "Compression store minimal or not needed currently" }} Analyze - CompressionEffectivenessMemory compression and paging to disk are complementary strategies, not replacements for each other. Understanding when each is used helps set appropriate expectations.
When Compression Is Used:
When Paging Is Used:
The Decision Flow:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
Memory Manager Page Eviction Decision:════════════════════════════════════════════════════════════════════════════ When a page must be evicted from a process's working set: ┌─────────────────────────────────────┐│ Page needs to be evicted │└─────────────┬───────────────────────┘ │ ▼┌─────────────────────────────────────┐│ Is page clean(unmodified) ? │└─────────┬─────────────┬─────────────┘ │ Yes │ No(Dirty) ▼ ▼┌─────────────────┐ ┌─────────────────────────────────┐│ Add to Standby │ │ Attempt compression ││ List directly │ └─────────────┬───────────────────┘│ (can be │ ││ reclaimed fast) │ ▼└─────────────────┘ ┌─────────────────────────────────┐ │ Does page compress well ? │ │ (Achieves ~2: 1 or better) │ └─────────┬─────────────┬─────────┘ │ Yes │ No ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ Store in │ │ Add to Modified │ │ Compression │ │ List for │ │ Store │ │ Write to Disk │ └────────┬────────┘ └─────────────────┘ │ ▼ ┌────────────────────────────────────┐ │ Is compression store getting full ? │ └────────┬─────────────┬─────────────┘ │ No │ Yes ▼ ▼ ┌─────────────────┐ ┌─────────────────────────┐ │ Keep in │ │ Oldest compressed pages │ │ compression │ │ written to paging file │ │ store │ │ (still compressed) │ └─────────────────┘ └─────────────────────────┘ Page Retrieval Priority:─────────────────────────────────────────────────────────────────────────1. Check if in Standby List(fast - soft fault)2. Check Compression Store(fast - decompress from RAM)3. Check Paging File(slow - disk I / O required) This hierarchy ensures that commonly accessed pages are retrievedquickly, while rarely - used pages may require slower disk access.Memory compression makes memory pressure more bearable, but it's not a substitute for adequate RAM. A system constantly at high compression is still under memory pressure—decompression takes CPU and adds latency versus active RAM access. If you see sustained high compression, consider adding memory or reducing workload.
Memory compression in Windows works automatically with no required configuration. However, understanding how to monitor, troubleshoot, and (rarely) adjust its behavior is valuable.
Monitoring Compression Health:
Signs of Compression Under Stress:
Signs You Need More RAM:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
# Comprehensive memory compression diagnostics function Get-CompressionDiagnostics { Write - Host "═══════════════════════════════════════════════════════════════════" - ForegroundColor Cyan Write - Host " MEMORY COMPRESSION DIAGNOSTICS " - ForegroundColor Cyan Write - Host "═══════════════════════════════════════════════════════════════════" - ForegroundColor Cyan Write - Host "" # System Memory Overview $os = Get - CimInstance Win32_OperatingSystem $cs = Get - CimInstance Win32_ComputerSystem $totalRAM = [math]:: Round($cs.TotalPhysicalMemory / 1GB, 2) $freeRAM = [math]:: Round($os.FreePhysicalMemory / 1KB / 1024, 2) $usedPct = [math]:: Round((1 - ($os.FreePhysicalMemory * 1KB / $cs.TotalPhysicalMemory)) * 100, 1) Write - Host "SYSTEM MEMORY" - ForegroundColor Yellow Write - Host "─────────────────────────────────────────────────" Write - Host "Total RAM: $totalRAM GB" Write - Host "Free RAM: $freeRAM GB" Write - Host "Usage: $usedPct%" Write - Host "" # Compression Store $compProc = Get - Process - Name "Memory Compression" - ErrorAction SilentlyContinue if ($compProc) { $compMB = [math]:: Round($compProc.WorkingSet64 / 1MB, 0) $compPct = [math]:: Round($compProc.WorkingSet64 / $cs.TotalPhysicalMemory * 100, 1) Write - Host "COMPRESSION STORE" - ForegroundColor Yellow Write - Host "─────────────────────────────────────────────────" Write - Host "Store Size: $compMB MB ($compPct% of RAM)" Write - Host "Est. Original: $([math]::Round($compMB * 2, 0)) MB (at 2:1 ratio)" Write - Host "Est. Savings: $([math]::Round($compMB, 0)) MB" if ($compPct - lt 5) { Write - Host "Status: Minimal compression (good)" - ForegroundColor Green } elseif($compPct - lt 15) { Write - Host "Status: Normal compression" - ForegroundColor Green } elseif($compPct - lt 30) { Write - Host "Status: Elevated compression (monitor)" - ForegroundColor Yellow } else { Write - Host "Status: High compression (consider more RAM)" - ForegroundColor Red } } else { Write - Host "COMPRESSION STORE" - ForegroundColor Yellow Write - Host "─────────────────────────────────────────────────" Write - Host "Status: Not active (sufficient free RAM)" - ForegroundColor Green } Write - Host "" # Paging Activity $pageCounters = Get - Counter - Counter @( 'MemoryPages/sec', 'MemoryPage Reads/sec', 'MemoryPage Writes/sec' ) -ErrorAction SilentlyContinue if ($pageCounters) { $samples = $pageCounters.CounterSamples $pagesSec = [math]:: Round(($samples | Where - Object { $_.Path - like '*Pages/sec' }).CookedValue, 0) $readsSec = [math]:: Round(($samples | Where - Object { $_.Path - like '*Page Reads*' }).CookedValue, 0) $writesSec = [math]:: Round(($samples | Where - Object { $_.Path - like '*Page Writes*' }).CookedValue, 0) Write - Host "PAGING ACTIVITY (current)" - ForegroundColor Yellow Write - Host "─────────────────────────────────────────────────" Write - Host "Pages/sec: $pagesSec" Write - Host "Disk Reads/sec: $readsSec" Write - Host "Disk Writes/sec: $writesSec" if ($readsSec - lt 10 - and $writesSec - lt 10) { Write - Host "Status: Low disk paging (excellent)" - ForegroundColor Green } elseif($readsSec - lt 50) { Write - Host "Status: Moderate disk paging" - ForegroundColor Yellow } else { Write - Host "Status: High disk paging (memory pressure)" - ForegroundColor Red } } Write - Host "" # Commit Status $commitLimit = $os.TotalVirtualMemorySize * 1KB $commitUsed = ($os.TotalVirtualMemorySize - $os.FreeVirtualMemory) * 1KB $commitPct = [math]:: Round($commitUsed / $commitLimit * 100, 1) Write - Host "COMMIT STATUS" - ForegroundColor Yellow Write - Host "─────────────────────────────────────────────────" Write - Host "Commit Limit: $([math]::Round($commitLimit / 1GB, 1)) GB" Write - Host "Commit Charge: $([math]::Round($commitUsed / 1GB, 1)) GB ($commitPct%)" if ($commitPct - lt 60) { Write - Host "Status: Healthy commit headroom" - ForegroundColor Green } elseif($commitPct - lt 80) { Write - Host "Status: Moderate commit usage" - ForegroundColor Yellow } else { Write - Host "Status: High commit (may exhaust)" - ForegroundColor Red } Write - Host "" Write - Host "═══════════════════════════════════════════════════════════════════" - ForegroundColor Cyan} # Run diagnosticsGet - CompressionDiagnostics # Quick recommendationsWrite - Host "`nRECOMMENDATIONS:" - ForegroundColor MagentaWrite - Host "• Low compression + low paging = Sufficient RAM"Write - Host "• High compression + low disk paging = Compression helping"Write - Host "• High compression + high disk paging = Need more RAM"Write - Host "• Very high commit % = Increase paging file or RAM"If you consistently see: (1) Memory Compression store > 25% of RAM, (2) High Page Reads/sec (> 50 sustained), (3) Available Memory < 500 MB, and (4) Visible performance impact—these are clear signs to add RAM. Compression is buying you time, but the system is under pressure.
We've explored Windows memory compression—the modern optimization layer that dramatically improves performance for memory-constrained scenarios. Let's consolidate the key takeaways:
Module Complete!
We've now covered all five core topics of Windows memory management:
Together, these concepts form the foundation of Windows memory management—knowledge essential for system administrators, performance engineers, and developers working on Windows platforms.
Congratulations! You now have comprehensive knowledge of Windows memory management—from virtual address spaces to compression stores. This foundation prepares you for advanced topics like memory forensics, kernel debugging, and system performance optimization.