Virtual memory is an abstraction provided by the operating system where each process sees a private, uniform, and contiguous memory space, regardless of the actual physical memory available. Virtual addresses used by your code are translated to physical addresses via a hardware component called the Memory Management Unit (MMU) using a page table. This translation is fast due to hardware caching via the TLB (Translation Lookaside Buffer).
Paging is the process of dividing memory into fixed-size blocks:
- Virtual memory → divided into pages (commonly 4KB each)
- Physical memory → divided into frames (same size)
A page table keeps track of which virtual page maps to which physical frame (if any). If the required page isn’t in physical memory → Page Fault occurs → OS loads it from disk (or errors out if invalid). Paging enables the OS to delay loading until the memory is actually accessed.
Main Memory is only used when accessed. We can use below program to check, here is a simple program, which creates a mmap memory, tries to access its 0th and 10th page, as each page is 4kB, so total use will be 8KB.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mmap_demo
LOCAL_SRC_FILES := mmap_demo.cpp
LOCAL_CFLAGS := -Wall -Werror -fno-omit-frame-pointer
LOCAL_CPPFLAGS := -std=c++17
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
int main() {
size_t page_size = getpagesize();
size_t num_pages = 10;
size_t total_size = page_size * num_pages;
std::cout << "System page size: " << page_size << " bytes\n";
std::cout << "Allocating " << total_size << " bytes (10 pages)...\n";
// Allocate memory using mmap (lazy allocation)
void* addr = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED) {
perror("mmap failed");
return 1;
}
std::cout << "Memory mapped at address: " << addr << "\n";
std::cout << "Touching only 1st and 10th page...\n";
char* base = static_cast<char*>(addr);
base[0] = 'A'; // Touch 1st page
base[9 * page_size] = 'Z'; // Touch 10th page
std::cout << "Now check memory usage:\n";
std::cout << " cat /proc/" << getpid() << "/smaps | grep -A10 " << addr << "\n";
std::cout << "Press ENTER to exit...\n";
std::cin.get();
munmap(addr, total_size);
return 0;
}
When you run above, you will get the command to run the other terminal to check status.
Memory mapped at address: 0x7f8c7b50000
Touching only 1st and 10th page...
Now check memory usage:
cat /proc/12345/smaps | grep -A10 0x7f8c7b50000
Run above command in terminal as below
sudo cat /proc/12345/smaps | grep -A20 7f8c7b50000 , Now you should see something like this
Rss: 8 kB ← 2 pages touched: 1st and 10th
Pss: 8 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 8 kB
Referenced: 8 kB
This shows , Virtual memory (mmap) allocates address space immediately. Physical memory is only committed when accessed.
Leave a comment