php之 Zend 内存管理器

发布时间:2024-01-26
zend 内存管理器
zend 内存管理器,经常缩写为 zendmm 或 zmm,是一个 c 层,旨在提供分配和释放动态请求绑定内存的能力。
注意上面句子中的“请求绑定”。
zendmm 不仅仅是 libc 的动态内存分配器上的一个经典层,主要由两个 api 调用 malloc()/free()表示。zendmm 是关于 php 在处理请求时必须分配的请求绑定内存。
相关学习推荐:php编程从入门到精通
php 中两种主要的动态内存池
php 是一个无共享架构。 well, not at 100%. let us explain.
注意
在继续之前,你可能需要阅读 php 生命周期章节,你将获得有关 php 生命周期中的不同步骤和周期的更多信息。
php可以在同一个进程中处理数百或数千个请求。默认情况下,php 会在完成当前请求后,忘记对当前请求的任何信息。
“忘记” 信息解释为释放处理请求时分配的任何动态缓冲区。这意味着在处理一个请求的过程中,不能使用传统的 libc 调用来分配动态内存。这样做是完全有效的,但是您给忘记释放缓冲区了机会。
zendmm 附带了一个 api,通过复制其 api 来替代 libc 的动态分配器。在处理请求的过程中,程序员必须使用该 api 而不是 libc 的分配器。
例如,当 php 处理请求时,它将解析 php 文件。例如,那些将导致函数和类的声明。当编译器开始编译 php 文件时,它将分配一些动态内存来存储它发现的类和函数。但是,在请求结束时,php 会释放这些。默认情况下,php 会忘记从一个请求到另一个请求的大量信息。
然而,存在一些非常罕见的信息,你需要持久地跨越多个请求。但这并不常见。
什么可以通过请求保持不变?我们所说的持久对象。再次说明:那是不常见的情况。例如,当前的 php 可执行路径不会在请求之间更改。其信息是永久分配的,这意味着它调用了 传统 libc 的 malloc ()来分配。
还有什么? 一些字符串。例如,“_server” 字符串将在请求之间重用,因为每个请求都将创建 $_server php 数组。所以 “_server” 字符串本身可以永久分配,因为它只会被分配一次。
你必须记住:
在编写 php 核心或扩展时,存在两种动态内存分配方式:
请求绑定的动态分配。永久动态分配。
请求绑定动态内存分配
仅在php处理请求时才执行(不在此之前或之后)。应该只使用 zendmm 动态内存分配 api 执行。在扩展设计中非常常见,基本上95%%u7684动态分配都是请求绑定的。由 zendmm 追踪,并会通知你有关泄漏的信息。
永久动态内存分配
不应该在php处理请求时执行(这不是禁止的,但是是一个坏主意)。不会被 zendmm 追踪,你也不会被告知泄漏。在扩展中应该很少见。
另外,请记住,所有 php 源代码都基于这种内存级别。因此,许多内部结构使用 zend 内存管理器进行分配。大多数都调用了一个“持久的” api,当调用这个时,将导致传统的 libc 分配。
这是一个请求绑定的分配 zend_string:
zend_string *foo = zend_string_init("foo", strlen("foo"), 0);这是持久分配的:
zend_string *foo = zend_string_init("foo", strlen("foo"), 1);同样的 hashtable。
请求绑定分配:
zend_array ar;zend_hash_init(&ar, 8, null, null, 0);持久分配:
zend_array ar;zend_hash_init(&ar, 8, null, null, 1);在所有不同的 zend api中,它始终是相同的。通常是作为最后一个参数传递的,“0”表示“我希望使用 zendmm 分配此结构,因此请求绑定”,或“1”表示“我希望通过 zendmm 调用传统的 libc 的malloc()分配此结构”。
显然,这些结构提供了一个 api,该 api 会记住它如何分配结构,以便在销毁时使用正确的释放函数。因此,在这样的代码中:
zend_string_release(foo);zend_hash_destroy(&ar);api 知道这些结构是使用请求绑定分配还是永久分配的,第一种情况将使用efree()释放它,第二种情况是libc的free()。
zend 内存管理器 api
该 api 位于 zend/zend_alloc.h
api 主要是 c 宏,而不是函数,因此,如果你调试它们并想了解它们的工作原理,请做好准备。这些 api 复制了 libc 的函数,通常在函数名称中添加“e”;因此,你不应该认错,关于该api的细节不多。
基本上,你最常使用的是 emalloc(size_t) 和efree(void *)。
还提供了ecalloc(size_t nmemb,size_t size),它分配单个大小size的nmemb,并将区域归零。如果你是一位经验丰富的 c 程序员,那么你应该知道,只要有可能,最好在emalloc()上使用ecalloc(),因为ecalloc()会将内存区域清零,这在指针错误检测中可能会有很大帮助。请记住,emalloc()的工作原理基本上与libc malloc()一样:它将在不同的池中寻找足够大的区域,并为你提供最合适的空间。因此,你可能会得到一个指向垃圾的回收指针。
然后是 safe_emalloc(size_t nmemb,size_t size,size_t offset),这是emalloc(size * nmemb offset),但它会为你检查溢出情况。如果必须提供的数字来自不受信任的来源(例如用户区),则应使用此api调用。
关于字符串,estrdup(char *) 和 estrndup(char *, size_t len) 允许复制字符串或二进制字符串。
无论发生什么,zendmm 返回的指针必须调用 zendmm 的efree() 释放,而不是 libc 的 free()。
注意
关于持久分配的说明。持久分配在请求之间保持有效。你通常使用常见的 libc malloc/ free 来执行此操作,但是 zendmm 有一些 libc 分配器的快捷方式:“持久” api。该 api以“p” 字母开头,让你在 zendmm 分配或持久分配之间进行选择。因此pemalloc(size_t, 1)不过是malloc(),pefree(void *, 1)是free() ,pestrdup(void *, 1) 是strdup()。只是说。
zend 内存管理器调试盾
zendmm 提供以下功能:
内存消耗管理。内存泄漏跟踪和自动释放。通过预分配已知大小的缓冲区并保持空闲状态下的热缓存来加快分配速度内存消耗管理
zendmm 是 php 用户区“memory_limit”功能的底层。使用 zendmm 层分配的每单个字节都会被计数并相加。当达到 ini 的 memory_limit 后,你知道会发生什么。这也意味着通过 zendmm 执行的任何分配都反映在 php 用户区的memory_get_usage()中。
作为扩展开发人员,这是一件好事,因为它有助于掌握 php 进程的堆大小。
如果启动了内存限制错误,则引擎将从当前代码位置释放到捕获块,然后平稳终止。但是它不可能回到超出限制的代码位置。你必须为此做好准备。
从理论上讲,这意味着 zendmm 无法向你返回 null 指针。如果从操作系统分配失败,或者分配产生内存限制错误,则代码将运行到 catch 块中,并且不会返回到你的分配调用。
如果出于任何原因需要绕过该保护,则必须使用传统的 libc 调用,例如malloc()。无论如何请小心,并且知道你在做什么。如果使用 zendmm,可能需要分配大量内存并可能超出 php 的 memory_limit。因此,请使用另一个分配器(如libc),但要注意:你的扩展将增加当前进程堆的大小。在 php 中不能看到 memory_get_usage(),但是可以通过使用 os 设施分析当前堆(如/proc/{pid}/maps)
注意
如果需要完全禁用 zendmm,则可以使用use_zend_alloc = 0环境变量启动php。这样,每次对 zendmm api的调用(例如emalloc())都将定向到 libc 调用,并且 zendmm 将被禁用。这在调试内存的情况下尤其有用。
内存泄漏追踪
请记住 zendmm 的主要规则:它在请求启动时启动,然后在你处理请求时需要动态内存时期望你调用其api。当前请求结束时,zendmm 关闭。
通过关闭,它将浏览其所有活动指针,如果使用 php 的调试构建,它将警告你有关内存泄漏的信息。
让我们解释得更清楚一些:如果在当前请求结束时,zendmm 找到了一些活动的内存块,则意味着这些内存块正在泄漏。请求结束时,z
上一个:酷比魔方i7bios进u盘,怎样设置BIOS进入U盘系统安装
下一个:法院判抚养费不给会怎样

数据中台选型,数据交换系统产品选型该如何选
开密室设计公司要多少钱
什么是自耦变压器?自耦变压器有哪些优点?
藏族人饮茶
代理合同的法律责任与无效的情形有哪些
轴承保持架结构及材料的选用
商用电脑哪款好,联想笔记本商用电脑哪款好
电缆故障测试仪有哪几部分组成及原理
g580 行车记录仪(行车记录仪停车监控触发灵敏度)
写过还款协议仍还不上怎么办