Fundamentals 47 min read

Don’t Claim Linux Memory‑Tuning Skills Without Mastering Memory Compaction

This article explains Linux memory compaction—from its core principles and page‑migration mechanics to the different compaction strategies, trigger conditions, practical test cases, and optimization tips—showing how proper compaction resolves fragmentation, improves allocation success, and boosts overall system performance.

Deepin Linux
Deepin Linux
Deepin Linux
Don’t Claim Linux Memory‑Tuning Skills Without Mastering Memory Compaction

1. Introduction to Memory Compaction

Long‑running Linux systems can exhibit abundant free memory yet fail to allocate large contiguous blocks, leading to OOM, process stalls, and service jitter. Most developers only check usage, cache, and swap, overlooking memory fragmentation, the root cause. Memory compaction, a kernel mechanism that merges scattered free pages into larger contiguous regions, is essential for high‑performance and long‑running services.

1.1 What Is Memory Compaction?

Memory compaction rearranges physical memory pages to eliminate external fragmentation, similar to reorganizing books on a shelf to create larger gaps. It operates within the kernel’s memory‑management subsystem and targets external fragmentation.

1.2 How Page Migration Works

Page migration is the core operation of compaction. The kernel allocates a new physical page, copies the data byte‑by‑byte, updates all page‑table entries that referenced the old page, and, if multiple processes map the page, synchronises their mappings. This process consumes time and resources, so compaction is triggered only when allocation difficulty justifies the cost.

1.3 Compaction Scope and Interface

Compaction is performed per zone (e.g., DMA, Normal, HighMem). The kernel exposes the compact_zone interface, which receives the zone pointer, priority, and desired block size, then attempts to coalesce free pages within that zone.

1.4 alloc_contig_range Special Case

The alloc_contig_range function targets a specific address range. When the range is occupied, it invokes a compaction‑like migration limited to that range, differing from zone‑wide compaction.

2. Fundamentals of Memory Compaction

2.1 Memory Fragmentation

Fragmentation occurs when repeated allocations and frees leave many small free blocks. External fragmentation prevents large allocations despite sufficient total free memory (e.g., free blocks of 2 KB, 3 KB, 5 KB cannot satisfy a 4 KB request). Internal fragmentation wastes space inside allocated blocks due to alignment or allocation granularity (e.g., an 8 KB block allocated for a 6 KB request).

2.2 Goals of Compaction

Compaction aims to eliminate external fragmentation, increase usable memory, and reduce allocation latency, which is critical for databases, graphics processing, and other memory‑intensive workloads.

2.3 Implementation Methods

Page Merging : Adjacent free pages are combined into a larger free region, reducing external fragmentation.

Physical Page Compression : Infrequently accessed pages are compressed (e.g., using LZ4 or ZSTD), shrinking their footprint and freeing space.

Page‑Table Compression : Multi‑level page tables are compacted, reducing the memory overhead of address translation.

Memory Reclamation : Unused pages are identified and returned to the free pool, preventing them from becoming isolated fragments.

3. Compaction Trigger Mechanisms

3.1 Direct Compaction

When a normal allocation fails, the allocator first calls get_page_from_freelist. If that fails, the slow‑path __alloc_pages_slowpath is entered, which may invoke __alloc_pages_direct_compact with INIT_COMPACT_PRIORITY. The initial attempt uses asynchronous migration (non‑blocking), but if it still fails, the kernel iterates with decreasing priority, eventually performing blocking compaction.

page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
page = __alloc_pages_slowpath(gfp_mask, order, ac);
page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, INIT_COMPACT_PRIORITY, &contended_compact);

3.2 Passive Compaction

When memory pressure rises (e.g., free memory falls below WMARK_HIGH), the kswapd kernel thread scans zones. If a zone is unbalanced, it calls compact_zone_order to compact pages, prioritising movable pages and freeing larger contiguous blocks.

if (!zone_balanced(zone, zonelist, order, 0, classzone_idx))
    compact_zone_order(zone, order, GFP_KERNEL, &contended);

3.3 Proactive Compaction

The kernel can predict future fragmentation and schedule compaction ahead of time:

if (should_proactive_compact(zone))
    queue_work(system_unbound_wq, &zone->compact_work);

3.4 Active Compaction

Admins or automated scripts can trigger global compaction via the sysfs interface:

sysfs_compact_memory() {
    compact_memory();
}

Active compaction follows the same page‑merging and reclamation steps as other methods.

4. Practical Case Study

4.1 Test Environment

Server: Intel Xeon E5‑2620 v4, 8 cores/16 threads, 2.1 GHz; 64 GB DDR4 2400 MHz; 512 GB SSD; Ubuntu 20.04 LTS (64‑bit). Monitoring tools: top, htop, vmstat, sar.

4.2 Reproducing Fragmentation

A C program allocates 1000 blocks of 32 bytes, frees every second block, then attempts a 5 KB allocation. The failure demonstrates fragmentation.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define ALLOC_COUNT 1000
#define SMALL_SIZE 32
#define LARGE_SIZE 5000
int main() {
    void *ptrs[ALLOC_COUNT];
    for (int i = 0; i < ALLOC_COUNT; i++) {
        ptrs[i] = malloc(SMALL_SIZE);
        if (!ptrs[i]) { perror("malloc small block failed"); return 1; }
        memset(ptrs[i], 0xAA, SMALL_SIZE);
    }
    for (int i = 0; i < ALLOC_COUNT; i += 2) {
        free(ptrs[i]);
        ptrs[i] = NULL;
    }
    void *large_block = malloc(LARGE_SIZE);
    if (!large_block) {
        printf("Memory fragmentation caused large block allocation failure
");
    } else {
        printf("Large block allocation succeeded
");
        free(large_block);
    }
    for (int i = 1; i < ALLOC_COUNT; i += 2) free(ptrs[i]);
    return 0;
}

4.3 Triggering Compaction

Before compaction, memory stats are captured via cat /proc/meminfo. Compaction is triggered by writing 1 to /proc/sys/vm/compact_memory (or sysctl -w vm.compact_memory=1). Kernel logs ( dmesg | grep -i compaction) show page migrations and the creation of larger free blocks.

# Record before state
before_meminfo=$(cat /proc/meminfo)
# Trigger compaction
sysctl -w vm.compact_memory=1
# Record after state
after_meminfo=$(cat /proc/meminfo)

4.4 Effect Analysis

After compaction, the largest free block grew from a few hundred bytes to several kilobytes, reducing the fragmentation index from ~0.8 to ~0.2. Performance tests showed allocation latency dropping from tens of milliseconds to a few milliseconds and CPU utilization decreasing from ~80 % to ~50 %.

5. Optimization Strategies

5.1 Tuning Memory Parameters

Adjusting watermarks ( WMARK_HIGH, WMARK_LOW, WMARK_MIN) influences when the kernel initiates reclamation and compaction. Higher high watermarks keep more free memory available, reducing fragmentation risk for latency‑sensitive services.

5.2 Improving Allocation Algorithms

Different allocators affect fragmentation:

First‑Fit is fast but creates many small holes.

Best‑Fit reduces leftover space but incurs higher search cost.

Buddy System (used by the Linux kernel) allocates in powers‑of‑two blocks; compaction helps merge buddies after frequent allocations.

Hybrid approaches—periodic free‑list consolidation or caching recent free blocks—can further mitigate fragmentation.

5.3 Regular Compaction Scheduling

Running compaction during low‑load windows (e.g., nightly) balances resource consumption against fragmentation buildup. The interval should consider workload patterns, memory‑pressure metrics, and acceptable overhead.

6. Interview Questions on Memory Compaction

6.1 What Triggers Compaction?

Allocation failure when a contiguous block is needed.

Memory pressure below watermarks.

Manual sysfs trigger.

6.2 Core Algorithm

Compaction scans a zone from both ends, identifies movable pages, copies them to free pages, updates page tables, and finally creates a large contiguous free region.

6.3 Relationship with Allocation Strategies

Compaction complements allocators by providing larger free chunks, especially for buddy‑system allocations where external fragmentation can still arise.

6.4 Differences Across OSes

Linux uses zone‑based buddy allocation with explicit compaction APIs; Windows integrates compaction into its virtual‑memory manager without exposing distinct strategies.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

KernelPerformance TuningLinuxMemory FragmentationMemory CompactionPage Migration
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.