Huge pages can increase the amount of memory covered by each cached address translation. They can reduce translation overhead for suitable workloads, but the result depends on access patterns and hardware. A large allocation alone does not prove frequent TLB misses, and fewer pages needed to describe a working set does not mean those translations all fit in the processor’s TLB.

Page size is an input

A 4 KiB base page is common on x86, but Linux page sizes depend on architecture and configuration. Query the running system rather than treating four kilobytes as universal. Supported huge-page sizes also vary; 2 MiB and 1 GiB are familiar examples, not a complete list for every processor.

For an idealized contiguous 8 GiB range, 4 KiB pages require 2,097,152 page-sized mappings, while 2 MiB pages require 4,096. That arithmetic describes coverage. It does not measure hardware TLB capacity, miss rate, page-walk latency, or the number of translations simultaneously resident. Different translation structures can also have different capacities for different page sizes.

Explicit pools and transparent allocation

Hugetlb provides explicitly requested huge pages backed by managed pools. Its persistent pages are unavailable for ordinary allocations and are not swapped out. Pools can be allocated at boot or adjusted later; surplus pages may also be allowed. Reserving at boot often makes contiguous allocation easier, but is not the only supported lifecycle.

Transparent Huge Pages use a different mechanism. Depending on kernel support and policy, larger anonymous-memory allocations can occur on page faults or through later collapse. THP is not merely a background process and does not require the administrator to provision a hugetlb pool. Modern kernels can support multiple THP sizes; do not equate every transparent allocation with one 2 MiB mapping.

The flexibility differs too. THP can fall back to smaller pages and can be split for operations such as reclaim. An explicit hugetlb mapping does not silently become an ordinary-page mapping; an application may separately choose a fallback allocation when its configuration permits. Distinguish the kernel mapping contract from the application’s optional behavior.

Budget capacity and placement

In /proc/meminfo, HugePages_Total describes the default-size pool, HugePages_Free counts currently unallocated pages, and HugePages_Rsvd counts commitments not yet allocated at fault time. Total multiplied by page size is pool capacity, not the amount promised by Rsvd. Reservations can account for part of the apparently free pool. Hugetlb covers all supported pool sizes, while per-size sysfs directories provide finer detail.

A sparse mapping can consume more resident memory than its useful data warrants. Pool capacity also competes with ordinary memory needs. Size from application requirements, verify actual mappings, and leave room for other allocations. Benefits should be demonstrated with representative throughput, latency, and translation measurements rather than inferred from a large configured pool.

When it breaks

Growing a pool can fail because the kernel cannot find suitably contiguous physical memory. Existing free huge pages are already contiguous, so a nonzero free counter is not itself evidence of fragmentation. Wrong page size, reservations, permissions, NUMA placement, or cgroup limits can instead explain why a particular application cannot use apparently available capacity.

NUMA policy can place pages away from the CPUs using them or restrict eligible nodes. Examine per-node allocation and access locality before blaming translation overhead. A translation-cache improvement can coexist with worse memory latency, and neither a global free counter nor a successful startup establishes good placement.

THP allocation, collapse, compaction, and splitting may affect latency differently. thp_collapse_alloc counts successful allocations during collapse; it is not a stall-duration counter. Correlate appropriate counter deltas with latency. Choosing madvise, always, or another supported per-size policy requires workload evidence, not a blanket rule that every database should use the same mode.

An 8 GiB page-count calculation is separated from TLB performance. hugetlb pools and THP differ; Total, Free and Rsvd counters have distinct meanings, and NUMA placement needs separate evidence. No article example was run for this graphic.
The 8 GiB page-count comparison is arithmetic; hugetlb reservations and THP behavior require separate interpretation.

Check it quickly

Save this as hugepage_check.py and run python3 hugepage_check.py. It reads counters and page size without allocating a huge-page workload or modifying kernel settings. Missing data produces an explicitly labeled arithmetic illustration. Parser and boundary assertions use synthetic strings.

import os
import re
from pathlib import Path

WORKING_SET = 8 * 1024**3
FIELDS = ("HugePages_Total", "HugePages_Free", "HugePages_Rsvd",
          "HugePages_Surp", "Hugepagesize")


def parse_meminfo(text):
    values = {}
    for key in FIELDS:
        unit = r"\s+kB" if key == "Hugepagesize" else ""
        match = re.search(r"^" + key + r":\s+(\d+)" + unit + r"\s*$", text, re.M)
        if match:
            values[key] = int(match.group(1))
    return values


def pages_for(size, page):
    if size < 0 or page <= 0:
        raise ValueError("invalid byte count or page size")
    return (size + page - 1) // page


assert pages_for(4097, 4096) == 2
assert parse_meminfo("HugePages_Total: 8\nHugepagesize: 2048 kB\n")["Hugepagesize"] == 2048
assert parse_meminfo("HugePages_Total: bad\nHugepagesize: 2 MB\n") == {}
try:
    pages_for(1, 0)
except ValueError:
    pass
else:
    raise AssertionError("zero page size accepted")

try:
    values = parse_meminfo(Path("/proc/meminfo").read_text())
except OSError as error:
    values = {}
    print("SKIP live hugetlb counters:", type(error).__name__)
try:
    base = os.sysconf("SC_PAGE_SIZE")
    assert base > 0
    print("Observed base page size:", base, "bytes")
except (AttributeError, ValueError, OSError):
    base = 4096
    print("Assumed illustration base page size:", base, "bytes")

if all(key in values for key in FIELDS) and values["Hugepagesize"] > 0:
    huge = values["Hugepagesize"] * 1024
    print("Default-size hugetlb pool counters:", values)
    print("Total pool capacity:", values["HugePages_Total"] * huge, "bytes")
    print("Reserved but not allocated:", values["HugePages_Rsvd"] * huge, "bytes")
else:
    huge = 2 * 1024**2
    print("SKIP complete pool report; assuming 2 MiB for arithmetic only")
print("8 GiB page-count illustration:", pages_for(WORKING_SET, base),
      "base pages versus", pages_for(WORKING_SET, huge), "large pages")
print("No TLB miss rate, THP coverage, allocation success, or latency was measured")

With 4 KiB base pages and a 2 MiB default huge-page size, expect the two page counts above. Zero pool totals are valid observations, not proof that the kernel lacks support. This revised script passed with Python 3.12.14 on Linux. It measures neither THP coverage nor performance; those require a separate, workload-specific experiment.

Further reading

Sources