热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

php之Zend内存管理器

Zend内存管理器,经常缩写为ZendMM或ZMM,是一个C层,旨在提供分配和释放动态请求绑定内存的能力。

请求绑定分配:

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),它分配单个大小sizenmemb,并将区域归零。如果你是一位经验丰富的 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 找到了一些活动的内存块,则意味着这些内存块正在泄漏。请求结束时,ZendMM 堆上不应存在任何活动内存块,因为分配了某些内存的任何人都应该释放了它们。

如果您忘记释放块,它们将全部显示在 stderr上。此内存泄漏报告进程仅在以下情况下有效:

  • 你正在使用 PHP 的调试构建
  • 在 php.ini 中具有 report_memleaks = On(默认)

这是一个简单泄漏到扩展中的示例:

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 对持久分配或以不同于使用持久分配的方式执行的分配一无所知。因此,ZendMM 只能警告你有关它知道的分配信息,在这里不会报告每个传统的 libc 分配信息。
  • 如果 PHP 以错误的方式关闭(我们称之为不正常关闭),ZendMM 将报告大量泄漏。这是因为引擎在错误关闭时会使用longjmp()调用 catch 块,防止清理所有内存的代码运行。因此,许多泄漏得到报告。尤其是在调用 PHP 的 exit()/ die()之后,或者在 PHP 的某些关键部分触发了致命错误时,就会发生这种情况。
  • 如果你使用非调试版本的 PHP,则 stderr 上不会显示任何内容,ZendMM 是愚蠢的,但仍会清除程序员尚未明确释放的所有分配的请求绑定缓冲区

你必须记住的是 ZendMM 泄漏跟踪是一个不错的奖励工具,但它不能代替真正的 C 内存调试器。

ZendMM 内部设计

常见错误和错误

这是使用 ZendMM 时最常见的错误,以及你应该怎么做。

  1. 不处理请求时使用 ZendMM。

获取有关 PHP 生命周期的信息,以了解在扩展中何时处理请求,何时不处理。如果在请求范围之外使用 ZendMM(例如在MINIT()中),在处理第一个请求之前,ZendMM 会静默清除分配,并且可能会使用after-after-free:根本没有。

  1. 缓冲区上溢和下溢。

使用内存调试器。如果你在 ZendMM 返回的内存区域以下或过去写入内容,则将覆盖关键的 ZendMM 结构并触发崩溃。如果 ZendMM 能够为你检测到混乱,则可能会显示“zend_mm_heap损坏”的消息。堆栈追踪将显示从某些代码到某些 ZendMM 代码的崩溃。ZendMM 代码不会自行崩溃。如果你在 ZendMM 代码中间崩溃,那很可能意味着你在某个地方弄乱了指针。插入你喜欢的内存调试器,查找有罪的部分并进行修复。

  1. 混合 API 调用

如果分配一个 ZendMM 指针(即emalloc())并使用 libc 释放它(free()),或相反的情况:你将崩溃。要严谨对待。另外,如果你将其不知道的任何指针传递给 ZendMM 的efree():将会崩溃。

以上就是php之 Zend 内存管理器的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 2023 ARM嵌入式系统全国技术巡讲旨在分享ARM公司在半导体知识产权(IP)领域的最新进展。作为全球领先的IP提供商,ARM在嵌入式处理器市场占据主导地位,其产品广泛应用于90%以上的嵌入式设备中。此次巡讲将邀请来自ARM、飞思卡尔以及华清远见教育集团的行业专家,共同探讨当前嵌入式系统的前沿技术和应用。 ... [详细]
  • 本文介绍如何解决在 IIS 环境下 PHP 页面无法找到的问题。主要步骤包括配置 Internet 信息服务管理器中的 ISAPI 扩展和 Active Server Pages 设置,确保 PHP 脚本能够正常运行。 ... [详细]
  • 探讨一个老旧 PHP MySQL 系统中,时间戳字段不定期出现异常值的问题及其可能原因。 ... [详细]
  • 程序员妻子吐槽:丈夫北漂8年终薪3万,存款情况令人意外
    一位程序员的妻子在网上分享了她丈夫在北京工作八年的经历,月薪仅3万元,存款情况却出乎意料。本文探讨了高学历人才在大城市的职场现状及生活压力。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 郑州大学在211高校中的地位与排名解析
    本文将详细解读郑州大学作为一所位于河南省的211和双一流B类高校,在全国211高校中的地位与排名,帮助高三学生更好地了解这所知名学府的实力与发展前景。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • 优化ASM字节码操作:简化类转换与移除冗余指令
    本文探讨如何利用ASM框架进行字节码操作,以优化现有类的转换过程,简化复杂的转换逻辑,并移除不必要的加0操作。通过这些技术手段,可以显著提升代码性能和可维护性。 ... [详细]
  • 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]
  • 电子元件封装库:三极管、MOS管及部分LDO(含3D模型)
    本资源汇集了常用的插件和贴片三极管、MOS管以及部分LDO的封装,涵盖TO和SOT系列。所有封装均配有高质量的3D模型,共计96种,满足日常设计需求。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
author-avatar
有你真好-LOVE
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有