Backend Development 10 min read

Does PHP’s unset() Really Free Memory? Deep Dive into Zend Memory Manager

This article examines whether PHP's unset() truly releases memory, debunks common myths, explains the distinction between language constructs and functions, explores memory_get_usage, and demonstrates through code examples how Zend Memory Manager handles variable cleanup under different scenarios.

360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
360 Zhihui Cloud Developer
Does PHP’s unset() Really Free Memory? Deep Dive into Zend Memory Manager

PHP provides unset() to release a variable, but does it actually free memory? This article investigates the question.

Common beliefs about unset()

It does not truly free memory.

It only frees memory when the variable exceeds 256 bytes.

Memory is freed only after all references are destroyed.

It only frees large variables (big strings, big arrays).

Is unset() a function?

Two verification attempts show it is not a regular function:

<code>$ php -r "var_dump(function_exists('unset'));"
bool(false)</code>
<code>$ php --rf unset
Exception: Function unset() does not exist</code>

These checks are not rigorous because both produce similar output when the function does not exist.

Language construct vs. function

In the PHP source, Zend/zend_language_scanner.l defines the token T_UNSET , indicating that unset is a language construct, not a callable function.

According to the manual, it is a language construct and cannot be invoked as a variable function.

What is a language construct?

PHP keyword

PHP identifier

Built‑in syntax rule

What is a function?

A block of code that performs a specific task.

Can be internal (built‑in), user‑defined, variable, or anonymous (closure).

Understanding memory_get_usage()

Prototype: memory_get_usage([ bool $real_usage = false ]) : int

If $real_usage is false , it returns the amount of memory actually used.

If $real_usage is true , it returns the total allocated memory, including unused portions.

Implementation (simplified):

<code>ZEND_API size_t zend_memory_usage(int real_usage) {
    #if ZEND_MM_STAT
    if (real_usage) {
        return AG(mm_heap)->real_size;
    } else {
        size_t usage = AG(mm_heap)->size;
        return usage;
    }
    #endif
    return 0;
}</code>

The function works only when Zend Memory Manager (ZMM) is enabled; otherwise PHP falls back to malloc() and the function returns a default value.

To temporarily disable ZMM:

<code>$ export USE_ZEND_ALLOC=0</code>

Example 1 – Small string variable

<code>&lt;?php
var_dump(memory_get_usage());
$user = 'fanjiapeng';
var_dump(memory_get_usage());
unset($user);
var_dump(memory_get_usage());
?></code>

Running the script yields identical memory usage values, showing that unset() did not reduce the reported memory.

When using memory_get_usage(true) , the values remain the same because PHP pre‑allocates memory and does not release it back to the system for small variables.

How a variable is stored

Allocate space for the variable name in the symbol table.

Allocate space for the variable value.

Bind name and value during the ZEND_RETURN phase.

ZMM allocates a large memory pool (e.g., 2 MB) at startup and serves allocation requests from this pool, returning memory to the pool but not to the OS.

What unset() actually does

Marks the variable value as deleted.

If reference counting applies, it may free the value's memory.

For interned strings (stored in interned_strings hash table), unset() does not free memory because they are not reference‑counted.

Interned strings are released when Opcache is shut down (NTS) or when Opcache is disabled, as illustrated below:

Example 2 – Temporary string

<code>&lt;?php
var_dump(memory_get_usage());
$user = 'fanjiapeng' . time();
var_dump(memory_get_usage());
unset($user);
var_dump(memory_get_usage());
?></code>

Here the memory usage decreases because the variable holds a temporary string (IS_TMP_VAR) whose memory is returned to ZMM.

Example 3 – Large string

<code>&lt;?php
var_dump(memory_get_usage(true));
$user = file_get_contents('/tmp/big_string.log'); // ~7.8 MB
var_dump(memory_get_usage(true));
unset($user);
var_dump(memory_get_usage(true));
?></code>

When the variable exceeds the huge‑memory threshold, ZMM allocates memory via mmap() ; unset() releases it with munmap() , reducing the process's memory footprint.

Summary

When ZMM is enabled and the release conditions are met:

unset() returns small or large variable memory to ZMM (not to the OS).

unset() frees huge variable memory directly.

When ZMM is disabled, PHP falls back to malloc() / free() , and unset() interacts directly with system memory, which is less efficient.

Memory Managementbackend developmentphpunsetZend Memory Manager
360 Zhihui Cloud Developer
Written by

360 Zhihui Cloud Developer

360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.

0 followers
Reader feedback

How this landed with the community

login 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.