Real Kernel RSS Memory Tracking
Standard PHP functions like memory_get_usage() and memory_get_peak_usage() only measure memory allocated inside the Zend Engine heap (the internal memory manager for PHP objects, arrays, and variables).
They are completely blind to memory allocated by:
- C-extensions (
curl,imagick,gd,openssl,ext-sodium) - Native glibc
malloc()/jemalloc/mimallocallocations - Embedded C/Go worker memory inside FrankenPHP
The Linux /proc/self/statm Interface
On Linux, the kernel maintains an ultra-fast pseudo-filesystem file for every process: /proc/self/statm.
Reading this file does not trigger disk I/O; the kernel directly exposes the process memory page table:
20480 12800 3840 256 0 10240 0Where the values represent:
- Total Program Size (Size): Total virtual memory size (in pages).
- Resident Set Size (RSS): Real physical RAM currently occupied by the process (in pages).
- Shared Pages (Shared): Pages mapped to shared libraries.
- Text / Code (Text): Executable code segment.
- Library (Lib): Shared library pages.
- Data + Stack (Data): Process data segment and user stack.
- Dirty Pages (Dirty): Modified physical memory pages.
How Leakless Reads and Computes RSS
- Kernel Page Resolution: Leakless dynamically resolves the Linux kernel page size using POSIX
posix_sysconf(POSIX_PC_SC_PAGESIZE)(typically4096bytes on x86_64, or65536bytes on certain ARM architectures). - Low-Overhead Parsing: During
startRequest()andendRequest(), Leakless reads/proc/self/statmwith zero-allocation string parsing (ProcStatmParser). - Conversion: Converts page counts directly to megabytes: $$\text{RSS (MB)} = \frac{\text{resident_pages} \times \text{page_size_bytes}}{1024 \times 1024}$$
- Memory Drift Calculation: Computes the exact physical memory delta ($\Delta \text{RSS}$) consumed during the active request cycle: $$\Delta \text{RSS} = \text{RSS}{\text{after}} - \text{RSS}{\text{before}}$$
If the Linux /proc filesystem is unavailable (such as during local development on macOS/Windows without Docker), Leakless automatically falls back to memory_get_usage(true) to ensure 100% portability.