Fundamentals 15 min read

How Linux and glibc Manage Memory: From Virtual Allocation to Physical Release

This article explains Linux's two‑level memory model, lazy physical allocation, the glibc malloc implementation, chunk metadata, large‑vs‑small allocations, memory holes, debugging hooks, leak detection with mtrace, and practical ways to measure a process's memory usage via /proc.

ITPUB
ITPUB
ITPUB
How Linux and glibc Manage Memory: From Virtual Allocation to Physical Release

Linux Memory Model

Linux separates memory into a virtual linear address space and physical pages. When a program calls malloc or similar, the kernel only reserves a range of virtual addresses; actual physical pages are allocated lazily, i.e., on the first access to each page.

Lazy Physical Allocation Example

char *p = malloc(2048); // reserves 2048 bytes of virtual memory only
strcpy(p, "123");   // first write touches the page, causing the kernel to allocate a physical page (still 2048 bytes of virtual space)
free(p);            // releases the physical page and the virtual region

Memory reservation and release are performed via system calls such as brk, sbrk, mmap and munmap. The process sees only virtual addresses; the kernel’s physical usage is transparent.

glibc Memory Allocator

glibc does not invoke a system call for every malloc / free. Instead it obtains a large chunk of virtual memory from the kernel:

For requests larger than 128 KB it uses mmap to map a separate region.

For smaller requests it expands the heap with brk / sbrk.

Subsequent allocations are satisfied from this internal pool, reducing the overhead of frequent system calls but introducing fragmentation.

Fragmentation and Heap Management

Irregular allocation sizes and frequent allocate/free cycles create holes. Small allocations (<128 KB) grow the heap upward using brk / sbrk. Large allocations are independent mmap regions, which do not guarantee upward growth. When the top of the heap contains a contiguous free block of at least 128 KB, glibc can shrink the heap by moving the break pointer, returning memory to the kernel.

Chunk Metadata

Each allocated block is preceded by a struct malloc_chunk that stores the block size. During free(p) the allocator reads the size at p‑4 (the metadata) and releases the block. glibc enforces a minimum allocation of 16 bytes to accommodate this metadata.

Debug Hooks and Leak Detection

glibc provides hook variables such as __malloc_hook and __malloc_initialize_hook that can be set to custom functions for tracing allocations. Memory‑leak detection is available via mtrace() and muntrace(). When MALLOC_TRACE points to a file, allocation and free events are logged; the supplied mtrace Perl script can analyse the log.

Example Program Using mtrace

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    int *p, *q;
#ifdef DEBUGGING
    mtrace();
#endif
    p = malloc(sizeof(int));
    q = malloc(sizeof(int));
    printf("p = %p
q = %p
", p, q);
    *p = 1;
    *q = 2;
    free(p);
    /* q is intentionally not freed to demonstrate a leak */
    return 0;
}

Running the program with MALLOC_TRACE=trace.log produces a log that records the allocation of p and q. Because q is never freed, the log reveals a leak.

Measuring Process Memory via /proc

Linux exposes memory statistics through the /proc filesystem. Relevant files: /proc/meminfo – system‑wide memory usage. /proc/<pid>/maps – virtual address mappings of a process. /proc/<pid>/statm – memory usage of a specific process (size, resident, shared, etc.).

Example output of cat /proc/self/statm shows page counts for total virtual size, resident set, shared pages, and more.

Key Fields in /proc/&lt;pid&gt;/statm

Size (VmSize) – total virtual address space (pages).

Resident (VmRSS) – physical memory actually in use (pages).

Shared – shared pages.

Trs – executable virtual memory (VmExe).

Lrs – size of mapped libraries (VmLib).

Drs – data + stack size (VmData + VmStk).

The free command reports total, used, free, buffers and cached memory. Linux keeps memory in buffers/cache for performance; this memory can be reclaimed instantly, so “available” memory is free + buffers + cached.

Dynamic Linking and Memory Footprint

Executable files (ELF) contain separate code (read‑only, executable) and data (read‑write) segments. The loader maps these segments with mmap, initially only reserving virtual memory. Physical pages are allocated when the code or data is accessed.

Two ways to link libraries:

Link at compile time – the loader maps the library at program start; the library’s pages remain resident for the process lifetime.

Load at runtime with dlopen / dlclose – the library can be unmapped, freeing its physical memory.

Runtime loading is advantageous for long‑running processes that only occasionally need certain libraries.

Conclusion

Understanding Linux’s lazy allocation, glibc’s internal allocator, and the /proc interfaces enables developers to write memory‑efficient programs, diagnose leaks with mtrace, and accurately assess a process’s memory consumption.

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.

DebuggingMemory Managementmallocglibcprocfs
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

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.