skip to content

In PHP, what do memory_get_usage() and memory_get_peak_usage() report, and what does passing true for $real_usage change?

level: middleimportance: should knowfreq 38%

answer

  1. current versus highest so far
  2. false: bytes handed to the script
  3. true: what the manager reserved
  4. memory_limit is checked on the reserved figure
  5. memory_reset_peak_usage() since 8.2

basics

~20 s

memory_get_usage() returns the bytes PHP's memory manager has allocated to the script now; memory_get_peak_usage() the highest so far. With $real_usage = true both report memory reserved from the system in chunks, the figure memory_limit is checked against.

solid answer

~40 s

Both functions report PHP's **own allocator**, not the operating system's view. `memory_get_usage()` is what is allocated right now; `memory_get_peak_usage()` is the high-water mark since the process started, or since the last `memory_reset_peak_usage()` (PHP 8.2+). The `$real_usage` flag switches the unit of account: `false`, the default, counts memory actually handed out to values, rounded to the allocator's size classes; `true` counts what the manager has reserved from the system in large chunks, including free space inside them — and that is the figure `memory_limit` is checked against. So `true` moves in big steps and rarely goes down, while `false` tracks your data closely. Memory an extension allocates directly with the system allocator is in neither figure, and the process's size in `top` is larger still.

go deeper

for a junior

Know that memory_get_usage() is the current figure and memory_get_peak_usage() the maximum so far, both in bytes.

for a middle

Explain the $real_usage flag — allocated values versus reserved chunks — and which figure memory_limit is enforced against.

for a senior

Instrument long jobs with periodic readings and memory_reset_peak_usage(), and reconcile PHP's figures with container metrics that include memory PHP does not track.

for a principal

Decide which memory signal the platform alerts on — PHP's peak or the container's resident size — and make budgets consistent with it.

## The four numbers PHP's memory manager requests memory from the operating system in large **chunks** and carves them into small blocks for strings, arrays and objects. The two functions expose both levels: | Call | Reports | |---|---| | `memory_get_usage()` | bytes currently allocated to the script's values | | `memory_get_usage(true)` | bytes currently reserved from the system by the manager | | `memory_get_peak_usage()` | highest value of the first figure so far | | `memory_get_peak_usage(true)` | highest value of the second figure so far | Both have the signature `(bool $real_usage = false): int` and return bytes. ## Which one to read - **Tracking your own data**: use the default (`false`). It rises when you build an array and falls when you `unset()` it, so it is the right number for "how much does this step keep?" - **Explaining a memory-limit error**: use `true`. The manual states that the reserved figure is what `memory_limit` is enforced against. It moves in steps of whole chunks, and freed space inside a chunk still counts until the manager returns the chunk. - **Reporting the cost of a job**: use `memory_get_peak_usage()` at the end. A job that peaks at 900 MB but ends at 20 MB still needed 900 MB. ## Peaks and resets Before PHP 8.2, the peak could only rise, so measuring the peak of one phase of a long script meant subtracting guesses. PHP 8.2 added `memory_reset_peak_usage()`: ```php memory_reset_peak_usage(); importBatch($rows); $batchPeak = memory_get_peak_usage(); ``` That measures one phase in isolation, which is useful in workers and long CLI jobs. ## What the numbers do not include 1. **Memory allocated outside PHP's manager.** The manual notes that persistent allocations and memory an extension takes directly with the system allocator are not reported, even with `$real_usage = true`. Some libraries do exactly that. 2. **The PHP binary, shared libraries and shared memory**, such as OPcache's segment. The operating system counts them in the process size; PHP's functions do not. 3. **Memory already returned to the system.** Chunks the manager frees go back to the OS, but the OS may report them lazily. This is why the resident size shown by `top` or a container dashboard is normally much larger than `memory_get_usage(true)`, and why the two can move independently. ## Why usage often does not fall after unset() `unset($bigArray)` lowers `memory_get_usage()` immediately if nothing else references the array. The `true` figure may not move: the freed blocks sit inside chunks that still hold other data, so the manager keeps the chunks. `gc_mem_caches()` asks the manager to release cached memory it no longer needs and returns the number of bytes freed; it is rarely necessary outside long-running processes. ## Common misreadings | Observation | What it actually means | |---|---| | `memory_get_usage()` is far below `memory_limit`, yet the limit error fires | the limit is checked on the reserved figure, which can be much higher than the allocated one | | the reserved figure never goes down during a job | chunks are kept while any part is in use, or cached for reuse | | the container kills the process while PHP reports little | memory outside PHP's allocator, plus the binary and shared memory, is counted by the OS only | | the peak is huge but usage at the end is small | a temporary structure existed at some point; find it with a peak reset around each phase | Reading the right figure avoids chasing the wrong problem: allocated usage answers "what does my data cost", reserved usage and its peak answer "why did the limit fire". ## A measuring habit Inside a loop over many records, log both figures every few thousand iterations: ```php if ($i % 10_000 === 0) { error_log(sprintf('%d rows, %.1f MB used, %.1f MB reserved', $i, memory_get_usage() / 1048576, memory_get_usage(true) / 1048576)); } ``` A flat line means the loop releases what it uses; a steady staircase means something retains data per iteration — the first thing to find before touching `memory_limit`.

  • Why can memory_get_usage(true) stay high after a large array is unset?
    The flag reports chunks the memory manager has reserved from the system. Freeing an array returns its blocks to the manager, but a chunk is only released when all of it is free, so the reserved figure often stays where it was while the default figure drops.
  • Why is a container's memory graph higher than memory_get_peak_usage(true)?
    The container counts the whole process: the PHP binary, shared libraries, shared memory such as OPcache, and memory extensions allocate outside PHP's manager. PHP's functions report only what its own allocator handles.

saying these in an interview costs you the question

  • memory_get_usage() reports the process size seen by the OS
  • memory_limit is checked against memory_get_usage() without the flag
  • The peak can never be reset in PHP 8.5
  • unset() always makes memory_get_usage(true) drop at once
  • Memory allocated by C libraries is included with $real_usage = true