Zend Memory Manager, often abbreviated to ZendMM or ZMM, is A C layer designed to provide the ability to allocate and free dynamic request-bound memory.
Pay attention to "request binding" in the above sentence.
ZendMM is not just a classic layer on top of libc's dynamic memory allocator, mainly represented by two API calls malloc()/free()
. ZendMM is about the request-bound memory that PHP must allocate when processing a request.
Related learning recommendations: PHP programming from entry to proficiency
PHP It is a shared nothing architecture. Well, not at 100%. Let us explain. More information on steps and cycles.
ZendMM comes with an API that replaces libc's dynamic allocator by copying its API. Programmers must use this API instead of libc's allocator when handling requests. For example, when PHP processes a request, it will parse the PHP file. For example, those will result in declarations of functions and classes. When the compiler starts compiling a PHP file, it will allocate some dynamic memory to store the classes and functions it discovers. However, PHP releases these at the end of the request. By default, PHP forgetsPHP can handle hundreds or thousands of requests in the same process. By default, PHP will forget any information about the current request after it is completed.
The "forgot" message is interpreted as freeing any dynamic buffers allocated while processing the request. This means that you cannot use traditional libc calls to allocate dynamic memory while processing a request. This is perfectly valid, but you give the opportunity to forget to free the buffer.
a lot of
information from one request to another. However, there are some very rare pieces of information that you need to persist across multiple requests. But this is not common.What can be kept unchanged by requests? What we call persistent
objects. Again: that's not a common situation. For example, the current PHP executable path does not change between requests. Its information is permanently allocated, which means it calls traditional libc'smalloc ()
to allocate it.what else? some string. For example, the "_SERVER" string will be reused between requests because a
$_SERVER
"_SERVER" the string itself can be permanently allocated, since it will only be allocated once. You must remember:
Permanent dynamic allocation.
Should only be performed using the ZendMM dynamic memory allocation API.
will not be tracked by ZendMM and you will not be notified of leaks.
zend_string *foo = zend_string_init("foo", strlen("foo"), 0);
zend_string *foo = zend_string_init("foo", strlen("foo"), 1);
Request bound allocation:
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 0);
Durable allocation:
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 1);
It is always the same across all the different Zend APIs. Usually passed as the last parameter,
"0"
"1"
means "I want to use ZendMM to allocate this structure, so the binding is requested", or"1" means "I want to use ZendMM to allocate this structure, so the binding is requested" Call traditional libc's malloc() to allocate this structure". Apparently these structures provide an API that remembers how it allocated the structure so that the correct release function is used when destroyed. So in code like this:
zend_string_release(foo); zend_hash_destroy(&ar);
efree()
and in the second The first situation is libc'sfree().
The API is located in Zend/zend_alloc.h
The API is mainly C macros, not functions, so if you debug them and want to understand their working Principle, please be prepared. These APIs copy libc's functions, often with an "e" added to the function name; therefore, you should not make the mistake, there are not many details about the API.
Basically, the ones you will use most often are emalloc(size_t)
and efree(void *)
.
Also provided is ecalloc(size_t nmemb, size_t size)
, which allocates a single nmemb
of size size
and zeroes out the region. If you are an experienced C programmer, then you should know that whenever possible, it is best to use ecalloc()
over emalloc()
because ecalloc ()
will zero out the memory area, which may be of great help in pointer error detection. Keep in mind that emalloc()
works basically the same as libc malloc()
: it will look for a large enough area in different pools and give you the most suitable Space. Therefore, you may end up with a garbage-collected pointer.
Then safe_emalloc(size_t nmemb, size_t size, size_t offset)
, which is emalloc(size * nmemb offset)
, but it will check for overflows for you . This API call should be used if the number that must be provided comes from an untrusted source (such as userland).
Regarding strings, estrdup(char *)
and estrndup(char *, size_t len)
allow copying string or binary strings.
No matter what happens, the pointer returned by ZendMM must be released by calling ZendMM's efree()
, and not libc's free().
Note
Instructions on persistent allocation. Durable allocations remain valid between requests. You usually use the usual libc
malloc/ free
to do this, but ZendMM has some shortcuts for the libc allocator: the "persistent" API. The API starts with "p" letters and lets you choose between ZendMM allocation or persistent allocation. Thereforepemalloc(size_t, 1)
is justmalloc()
,pefree(void *, 1)
isfree()
,pestrdup(void *, 1)
isstrdup()
. Just saying.
ZendMM provides the following features:
ZendMM is a PHP userland Underlying the "memory_limit" function. Every single byte allocated using the ZendMM layer is counted and summed. You know what happens when the INI's memory_limit is reached. This also means that any allocations performed via ZendMM are reflected in memory_get_usage()
in the PHP userland.
As an extension developer, this is a good thing because it helps keep track of the heap size of your PHP process.
If a memory limit error is initiated, the engine will release from the current code position into the capture block and then terminate gracefully. But it's not possible to get back to a code location that exceeds the limit. You have to be prepared for this.
Theoretically, this means that ZendMM cannot return a NULL pointer to you. If the allocation from the operating system fails, or the allocation produces a memory limit error, the code will run into a catch block and will not return to your allocation call.
If for any reason you need to bypass this protection, you must use traditional libc calls such as malloc()
. At any rate please be careful and know what you are doing. If you use ZendMM, you may need to allocate a large amount of memory and may exceed PHP's memory_limit. So use another allocator (like libc), but beware: your extension will increase the current process heap size. memory_get_usage()
cannot be seen in PHP, but the current heap can be analyzed by using OS facilities (such as /proc/{pid}/maps)
Note
If you need to completely disable ZendMM, you can use the
USE_ZEND_ALLOC = 0
environment variable to start PHP. This way, every call to the ZendMM API (such as emalloc()) will be directed to the libc call, and ZendMM will be disabled. This is especially useful in the case of debugging memory.
Remember the main rule of ZendMM: it starts when the request starts, and then expects you to call its API when you need dynamic memory to handle the request. When the current request ends, ZendMM shuts down.
By closing, it will browse all its live pointers and, if using PHP's debug build, it will warn you about memory leaks.
Let us explain it a little clearer: If at the end of the current request, ZendMM finds some active memory blocks, it means that these memory blocks are leaking. There shouldn't be any active blocks of memory on the ZendMM heap at the end of the request, because whoever allocated some memory should have freed it.
如果您忘记释放块,它们将全部显示在 stderr上。此内存泄漏报告进程仅在以下情况下有效:
这是一个简单泄漏到扩展中的示例:
PHP_RINIT_FUNCTION(example) { void *foo = emalloc(128); }
在启动该扩展的情况下启动 PHP,在调试版本上会在 stderr 上生成:
[Fri Jun 9 16:04:59 2017] Script: '/tmp/foobar.php' /path/to/extension/file.c(123) : Freeing 0x00007fffeee65000 (128 bytes), script=/tmp/foobar.php === Total 1 memory leaks detected ===
当 Zend 内存管理器关闭时,在每个已处理请求的末尾,将生成这些行。
但是要当心:
你必须记住的是 ZendMM 泄漏跟踪是一个不错的奖励工具,但它不能代替真正的 C 内存调试器。
这是使用 ZendMM 时最常见的错误,以及你应该怎么做。
获取有关 PHP 生命周期的信息,以了解在扩展中何时处理请求,何时不处理。如果在请求范围之外使用 ZendMM(例如在MINIT()中),在处理第一个请求之前,ZendMM 会静默清除分配,并且可能会使用after-after-free:根本没有。
使用内存调试器。如果你在 ZendMM 返回的内存区域以下或过去写入内容,则将覆盖关键的 ZendMM 结构并触发崩溃。如果 ZendMM 能够为你检测到混乱,则可能会显示“zend_mm_heap损坏”的消息。堆栈追踪将显示从某些代码到某些 ZendMM 代码的崩溃。ZendMM 代码不会自行崩溃。如果你在 ZendMM 代码中间崩溃,那很可能意味着你在某个地方弄乱了指针。插入你喜欢的内存调试器,查找有罪的部分并进行修复。
如果分配一个 ZendMM 指针(即emalloc()
)并使用 libc 释放它(free()
),或相反的情况:你将崩溃。要严谨对待。另外,如果你将其不知道的任何指针传递给 ZendMM 的efree()
:将会崩溃。
The above is the detailed content of Zend memory manager for php. For more information, please follow other related articles on the PHP Chinese website!